HDU3920:Clear All of Them I
——有2*n个敌人。你可以射击n次,每次可以且必须消灭2个敌人,消耗的能量为你到第一个敌人和第一个敌人到第二个敌人的距离。问消耗的能量最小是多少。
——dp,状态压缩
——url:http://acm.hdu.edu.cn/showproblem.php?pid=3920
——————————————————————————————————————————————————————
dp[I][J]表示第I次射击,敌人的情况为J时消耗的最小能量(J为二进制压缩的数)。
dp[I][J]=MIN(DP[I-1][K&(1<<x)&(1<<y]+dist,DP[I][J]) (x,y为第i次消灭的敌人)
如此这个问题就解决了,但是此题时限比较紧,因此用这个方程会超时。
——————————————————————————————————
下面是些优化:
1、由于死亡的敌人多的状态必然在多的之后,因此方程可以化解为一维。
例如110110这个状态必然在000110和110110之后。
于是方程变为dp[J]=MIN(DP[K&(1<<x)&(1<<y]+dist,DP[J])
2、由于此题每个敌人最后必然会被消灭,因此对于特定的两个敌人,我在这一次消灭和在下一次消灭并没有区别,因此转移从二维降低到一维。
k和l的循环不必嵌套。当前的k必定要被消灭,在哪一轮消灭无所谓,只要找到和它一同消灭的那个敌人就可以了。
【具体参见代码】
View Code
1 #include<stdio.h>
2 #include<math.h>
3 #define oo 0x7ffffff
4 #define N 12
5 double dp[1 << 20], dis[N*2][N*2];
6 int x[N * 2], y[N*2];
7 int xx, yy, i, j, k, l, o, cas, n, t;
8 double tmp;
9 double cal(int a, int b, int c, int d)
10 {
11 double x1 = (double) a, y1 = (double) b, x2 = (double) c, y2 = (double) d;
12 return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
13 }
14 double min(double a, double b)
15 {
16 return a > b ? b : a;
17 }
18 int main()
19 {
20 scanf("%d", &cas);
21 for (t = 1; t <= cas; t++)
22 {
23 scanf("%d%d", &xx, &yy);
24 scanf("%d", &n);
25 for (i = 0; i < 2 * n; i++)
26 scanf("%d%d", &x[i], &y[i]);
27 for (i = 0; i < 2 * n; i++)
28 {
29 dis[i][2 * n] = cal(x[i], y[i], xx, yy);
30 for (j = 0; j < 2 * n; j++)
31 dis[i][j] = dis[j][i] = cal(x[i], y[i], x[j], y[j]);
32 }
33 for (j = 0; j <= (1 << n * 2) - 1; j++) //优化1,只枚举J即可。
34 dp[j] = oo;
35 dp[0] = 0.0;
36 for (j = 0; j <= (1 << n * 2) - 1; j++)
37 if (dp[j] < oo)
38 {
39 for (k = 0; k < 2 * n; k++) //优化2,k和l的循环不必嵌套。当前的k必定要被消灭,在哪一轮消灭无所谓,只要找到和它一同消灭的那个敌人就可以了。
40 if ((j & (1 << k)) == 0)
41 break;
42 for (l = k + 1; l < 2 * n; l++)
43 if ((j & (1 << l)) == 0)
44 {
45 tmp = min(dis[k][2 * n] + dis[l][k],
46 dis[l][2 * n] + dis[l][k]);
47 o = j + (1 << k) + (1 << l);
48 dp[o] = min(dp[o], tmp + dp[j]);
49 }
50 }
51 printf("Case #%d: %.2lf\n", t, dp[(1 << n * 2) - 1]);
52 }
53 return 0;
54 }