状态压缩dp可太难了 但大体上和状态机感觉适基于差不多的思想
这种dp除了能够用某种图形不重叠地填满一张图 还可以用于检测是否所有的要求都达到了状态(这话说的我自己都不知道在说什么
https://www.luogu.com.cn/problem/P2831 noip 2016 愤怒的小鸟
这道题正如其名 给了我们一张图 上面有很多点(猪), 问我们最少能使用多少个小鸟能个将所有的猪打死
所有的小鸟在发射的时候都形如一个 y = ax^2 + bx 的抛物线 所以我们可以用任意两只猪所构成的抛物线来代表我们发射的一只小鸟
我们可以进行预处理 num[i, j]表示经过第i和第j点的一只鸟所经过的点的状态
即先计算出i和j构成的抛物线 再捋一遍所有的点 假如其位于线上 就将该位置置为1
而由于伟大的物理学(雾 其所构成的抛物线一定是开口向下的 即 a < 0 所以我们就需要自动跳过 a >= 0 的线
这样预处理就进行完了 下面就是状态压缩的部分了
f[i] 表示状态为 i 的图所需要的抛物线数量的最小值 自然而然我们在计算之前需要将所有的状态置为正无穷(0 <= i < (1 << n) )
对于每一个i 我们需要检测其上每一个没有经过的点j 将所有任何一个的带有j的抛物线(num[j, 0 ~ n])加入该图 得到 i | num[j, k]
之后就可以检测构成该图的抛物线数量是否为当前最优 即f[i | num[j][k]] = min(f[i|num[j][k]], f[i] + 1)
最后将所有状态计算完得到的f[(1 << n) - 1] 即最后答案
最后说点废话emmmm 由于double有精度差别 我在做这道题的时候把精度只开到了1e-5被一个点卡了半天 吐了
const int N = 18, M = 1 << N;
double x[N], y[N];
int f[M], n, num[N][N], t;
int T;
bool cmp(double a, double b)
{
return fabs(a - b) <= 1e-6;
}
int main(void)
{
cin >> T;
while(T -- )
{
cin >> n >> t;
memset(f, 0x3f,sizeof f); f[0] = 0;
memset(num, 0, sizeof num);
for(int i = 0; i < n; i ++ ) cin >> x[i] >> y[i];
for(int i = 0; i < n; i ++ )
{
num[i][i] = 1 << i;
for(int j = 0; j < n; j ++ )
{
double x1 = x[i], x2 = x[j], y1 = y[i], y2 = y[j];
double a = (y1/x1 - y2/x2)/(x1 - x2), b = y1/x1 - a * x1;
if(a >= 0 || cmp(x1, x2)) continue;
for(int k = 0; k < n; k ++ )
if(cmp(a * x[k] * x[k] + b * x[k], y[k]))
num[i][j] += 1 << k;
}
}
for(int i = 0; i < 1 << n; i ++ )
for(int j = 0; j < n; j ++ )
if(!(i & (1 << j)))
for(int k = 0; k < n; k ++ )
f[i | num[j][k]] = min(f[i|num[j][k]], f[i] + 1);
cout << f[(1 << n) - 1] << endl;
//for(int i = 0; i < 1 << n; i ++ ) cout << f[i] << ' ';
}
return 0;
}
标签:状态,int,压缩,小鸟,num,抛物线,dp 来源: https://www.cnblogs.com/yzzlqyxc/p/14481437.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。