[NOIP2016]愤怒的小鸟 DP

~~~题面~~~

题解:

  首先观察数据范围,n <= 18,很明显是状压DP。所以设f[i]表示状态为i时的最小代价。然后考虑转移。

  注意到出发点(0, 0)已经被固定,因此只需要2点就可以确定一条抛物线,所以每次转移时枚举是哪两只猪确定了这条抛物线,然后由于一条抛物线可能会恰好打中别的猪,所以再枚举判断一下哪些猪会被打中,然后就获得了一个后续状态,直接转移即可。

  但是这样是$2^nn^3T$的复杂度,显然过不去,因此考虑优化。

  1,因为一旦确定抛物线的2只猪确定了,这条抛物线会经过哪些其他的猪也就确定了,所以我们可以预处理出g[i][j],表示用第i和第j只猪确定抛物线时总共可以打到哪些猪。

  2,因为观察到对于2条抛物线,先发射哪条不影响答案,同理,因为所有猪都必须被打,所以那只猪先被打掉也不影响答案,所以每次转移时只打状态中第一个没被打的猪,然后就可以break进入下一个状态了。因为这次没打的猪,下次还是会打掉的,因此不影响正确性。

  于是复杂度$2^nnT$

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define eps 1e-9
  5 #define AC 20
  6 #define ac 300000
  7 #define ld long double
  8 
  9 int T, n, m, maxn;
 10 int f[ac], g[AC][AC];
 11 struct node{
 12     ld x, y;
 13 }pig[AC], func;
 14 
 15 inline int read()
 16 {
 17     int x = 0;char c = getchar();
 18     while(c > '9' || c < '0') c = getchar();
 19     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 20     return x;
 21 }
 22 
 23 node cal(node x, node y)//计算解析式
 24 {
 25     ld a, b;
 26     a = (x.y * y.x - y.y * x.x) / (x.x * x.x * y.x - y.x * y.x * x.x);
 27     b = x.y / x.x - a * x.x;
 28     return (node){a, b};
 29 }
 30 
 31 inline void upmin(int &a, int b)
 32 {
 33     if(b < a) a = b;
 34 }
 35 
 36 bool check(node ff, node x)//计算一个点是否过解析式
 37 {
 38     ld a = ff.x, b = ff.y;
 39     return (fabs(x.y - (a * x.x * x.x + b * x.x)) <= eps);
 40 }
 41 
 42 void pre()
 43 {
 44     int x = 1, tmp;
 45     n = read(), m = read();
 46     maxn = (1 << n) - 1;
 47     memset(g, 0, sizeof(g));
 48     memset(f, 127, sizeof(f));
 49     f[0] = 0;
 50     for(R i = 1; i <= n; i ++) 
 51         scanf("%Lf%Lf", &pig[i].x, &pig[i].y);
 52     for(R i = 1; i <= n; i ++, x <<= 1)
 53     {
 54         int now = 1;
 55         for(R j = 1; j <= n; j ++, now <<= 1)
 56         {
 57             if(i == j) {g[i][j] = x; continue;}
 58             tmp = x | now; func = cal(pig[i], pig[j]);
 59             if(func.x >= 0) {g[i][j] = 0; continue;}//不合法
 60             int t = 1;
 61             for(R k = 1; k <= n; k ++, t <<= 1)
 62             {
 63                 if(k == i || k == j) continue;
 64                 if(!check(func, pig[k])) continue;
 65                 tmp |= t;
 66             }
 67             g[i][j] = tmp;
 68         }
 69     }
 70 }
 71 
 72 void get()
 73 {
 74     for(R i = 0; i <= maxn; i ++)
 75     {
 76         int x = 1;
 77         for(R j = 1; j <= n; j ++, x <<= 1)
 78         {
 79             if(i & x) continue;
 80             for(R k = 1; k <= n; k ++)
 81                 upmin(f[i | g[j][k]], f[i] + 1);
 82             break;
 83         }
 84     }
 85     printf("%d\n", f[maxn]);
 86 }
 87 
 88 void work()
 89 {
 90     T = read();
 91     while(T --) 
 92         pre(), get();
 93 }
 94 
 95 int main()
 96 {
 97 //    freopen("in.in", "r", stdin);
 98     work();
 99 //    fclose(stdin);
100     return 0;
101 }
View Code

 

posted @ 2018-10-09 15:19  ww3113306  阅读(208)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。