[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 }