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 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
yxc老师的代码

 

posted @ 2021-04-03 15:32  acmloser  阅读(106)  评论(0编辑  收藏  举报