P2831 [NOIP2016 提高组] 愤怒的小鸟
考察:状压dp
重复覆盖模型,最优解是用dancing links.这里dp可以比暴力dfs稍微优化.
本蒟蒻的思路:
枚举两个点(横坐标不能相同)构造一条抛物线.再对每一条抛物线看是否还有点能被该抛物线覆盖.最后dp求到达(1<<n)-1的最短距离.
除法一定要注意判0
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int N = 18,INF = 0x3f3f3f3f; 7 const double eps = 1e-8; 8 typedef pair<double,double> PDD; 9 struct FX{ 10 double a,b; 11 int st; 12 }fx[N*N]; 13 PDD p[N]; 14 int n,m,cnt,f[1<<N]; 15 PDD get(double x1,double x2,double y1,double y2) 16 { 17 PDD s; 18 s.first = (x2*y1-x1*y2)/(x1*x1*x2-x2*x2*x1); 19 s.second = (x2*x2*y1-x1*x1*y2)/(x1*x2*x2-x2*x1*x1); 20 return s; 21 } 22 void init() 23 { 24 for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].first,&p[i].second); 25 for(int i=0;i<n;i++) 26 for(int j=i+1;j<n;j++) 27 { 28 if(fabs(p[i].first-p[j].first)<=0) continue; 29 double a = get(p[i].first,p[j].first,p[i].second,p[j].second).first; 30 double b = get(p[i].first,p[j].first,p[i].second,p[j].second).second; 31 if(a<0) 32 { 33 fx[++cnt].a = a; 34 fx[cnt].b = b; 35 fx[cnt].st |= (1<<i)|(1<<j); 36 } 37 } 38 for(int i=1;i<=cnt;i++) 39 for(int j=0;j<n;j++) 40 { 41 double x = p[j].first,y = p[j].second; 42 if(fabs(y-fx[i].a*x*x-fx[i].b*x)<=eps) 43 fx[i].st|=(1<<j); 44 } 45 } 46 int solve() 47 { 48 memset(f,0x3f,sizeof f); 49 f[0] = 0; 50 for(int i=0;i<1<<n;i++) 51 for(int j=1;j<=cnt;j++) 52 { 53 int p = fx[j].st; 54 f[i|p] = min(f[i]+1,f[i|p]); 55 } 56 return f[(1<<n)-1]; 57 } 58 int main() 59 { 60 int T; 61 scanf("%d",&T); 62 while(T--) 63 { 64 scanf("%d%d",&n,&m); 65 cnt = 0; 66 memset(fx,0,sizeof fx); 67 init(); 68 int res = solve(); 69 if(res>=INF) 70 { 71 int tar = 0,ans = 0; 72 res = 0; 73 for(int i=(1<<n)-1;i;i--) 74 if(f[i]<INF) {tar = i;break;} 75 for(int i=0;i<n;i++) 76 if(!(tar>>i&1)) ans++; 77 res = f[tar]+ans; 78 } 79 printf("%d\n",res); 80 } 81 return 0; 82 }
大佬的思路:
参考A*算法模拟dp.利用二维数组表示抛物线覆盖的两点(初始化要至少覆盖一个点)对于每一个抛物线再跑一遍能覆盖的点.这样就计算完了抛物线.最后计算最短距离有点不同.对于每一个枚举的状态i,我们先找到第一个它没有覆盖的点.再枚举能够覆盖它的抛物线.这样就计算覆盖此点的最小距离.最后求出f[(1<<n)-1].
这样算的好处是不用担心f[(1<<n)-1]没有被更新到的问题.因为抛物线至少覆盖了一个点.
懒得写一遍了,以后可能复习会写一遍.
1 #include <cstring> 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 6 #define x first 7 #define y second 8 9 using namespace std; 10 11 typedef pair<double, double> PDD; 12 13 const int N = 18, M = 1 << 18; 14 const double eps = 1e-8; 15 16 int n, m; 17 PDD q[N]; 18 int path[N][N]; 19 int f[M]; 20 21 int cmp(double x, double y) 22 { 23 if (fabs(x - y) < eps) return 0; 24 if (x < y) return -1; 25 return 1; 26 } 27 28 int main() 29 { 30 int T; 31 cin >> T; 32 while (T -- ) 33 { 34 cin >> n >> m; 35 for (int i = 0; i < n; i ++ ) cin >> q[i].x >> q[i].y; 36 37 memset(path, 0, sizeof path); 38 for (int i = 0; i < n; i ++ ) 39 { 40 path[i][i] = 1 << i; 41 for (int j = 0; j < n; j ++ ) 42 { 43 double x1 = q[i].x, y1 = q[i].y; 44 double x2 = q[j].x, y2 = q[j].y; 45 if (!cmp(x1, x2)) continue; 46 double a = (y1 / x1 - y2 / x2) / (x1 - x2); 47 double b = y1 / x1 - a * x1; 48 49 if (cmp(a, 0) >= 0) continue; 50 int state = 0; 51 for (int k = 0; k < n; k ++ ) 52 { 53 double x = q[k].x, y = q[k].y; 54 if (!cmp(a * x * x + b * x, y)) state += 1 << k; 55 } 56 path[i][j] = state; 57 } 58 } 59 60 memset(f, 0x3f, sizeof f); 61 f[0] = 0; 62 for (int i = 0; i + 1 < 1 << n; i ++ ) 63 { 64 int x = 0; 65 for (int j = 0; j < n; j ++ ) 66 if (!(i >> j & 1)) 67 { 68 x = j; 69 break; 70 } 71 72 for (int j = 0; j < n; j ++ ) 73 f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1); 74 } 75 76 cout << f[(1 << n) - 1] << endl; 77 } 78 79 return 0; 80 } 81 82 作者:yxc 83 链接:https://www.acwing.com/solution/content/4028/ 84 来源:AcWing 85 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。