2 0 0 1 6 0 3 0 0 0 2 1 0 2 1 -1 0 -2 0
Case #1: 6.00 Case #2: 4.41解题思路:首先这道题目最多只有20个状态,打中目标还有考虑别的目标是否打中,属于取与不取的问题,那么正好就是状态压缩能够解决的范畴。。dp[i]表示状态为i时的最小化费,那么一次能够打两个,那么下一个状态应该是i-(1<<j)-(1<<k),j,k分别代表先后击中的目标。最后的目标是dp[0](0代表该位置的目标被击中,1代表未被击中)。可惜最后TLE,后面仔细想想,应该还是在先被击中与后被击中的选择上循环次数太多啦。。。TLE:#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; const int maxn = 55; const double esp = 1e-8; struct node { double x,y; }o,s[maxn]; int n; double dp[(1<<20)+5],d[maxn][maxn],os[maxn]; double dis(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main() { int t,i,j,k,cas = 1; scanf("%d",&t); while(t--) { scanf("%lf%lf%d",&o.x,&o.y,&n); n *= 2; for(i = 0; i<n; i++) scanf("%lf%lf",&s[i].x,&s[i].y); for(i = 0; i<n; i++) for(j = 0; j<n; j++) { d[i][j] = dis(s[i],s[j]); } for(i = 0; i<n; i++) os[i] = dis(o,s[i]); for(i = 0; i<(1<<n); i++) dp[i] = -1.0; dp[(1<<n)-1] = 0; for(i = (1<<n)-1; i >= 0; i--) for(j = 0; j < n; j++) //第一次打到j for(k = 0; k < n; k++) //第二次达到k { if(dp[i] == -1) continue; if(i & (1<<j) && i & (1<<k) && j != k) { int tmp = i - (1<<j) - (1<<k); if(dp[tmp] < esp || dp[tmp] > dp[i] + os[j] + d[j][k]) dp[tmp] = dp[i] + os[j] + d[j][k]; } } printf("Case #%d: %.2f\n",cas++,dp[0]); } return 0; }
看了别人的分析:所以状态的转移是由前往后递推的,假如当前状态是cur,上一个状态是last,应该满足存在i!=j有last&(1<<i)=0且last&(1<<j)=0且last | (1<<i)|(1<<j) = cur,由此来更新当前的状态cur,最后的答案就是DP[(1<<(2n)) - 1]
注意到上面我们是要枚举i和j的,所以这个的总复杂度就是20 * 20 * 2^20,这显然是会超时的, 所以需要优化
注意到如果存在两队人(a,b)(c,d)我们先打(a, b)再打(c, d)和先打(c,d)在打(a, b)是一样的,所以我们完全可以每次取last中最小的一位是0的与后面所有的0组合,这样不仅没有漏掉解,而且复杂度也将到了O(20 * 2^20)这就可以过了
AC:#include <stdio.h> #include <string.h> #include <math.h> #include <algorithm> using namespace std; #define exp 1e-8 struct node { double x,y; } o,s[50]; int n; double d[50][50],dp[(1<<20)+1],os[50]; double dis(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int cmp(node a,node b) { return dis(a,o)<dis(b,o); } double dfs(int u) { if(dp[u]>exp) return dp[u]; if(!u) return 0; int m = 0; while(!(u&(1<<m))) m++; int tem; for(int i = m+1; i<n; i++) { if(u&(1<<i)) { tem = u - (1<<i) - (1<<m); double ans = dfs(tem)+os[m]+d[i][m]; if(dp[u]<exp || ans<dp[u]) dp[u] = ans; } } return dp[u]<exp?0:dp[u]; } int main() { int t,i,j,k,cas = 1; scanf("%d",&t); while(t--) { scanf("%lf%lf%d",&o.x,&o.y,&n); n*=2; for(i = 0; i<n; i++) scanf("%lf%lf",&s[i].x,&s[i].y); sort(s,s+n,cmp); for(i = 0; i<n; i++) for(j = 0; j<n; j++) { d[i][j] = dis(s[i],s[j]); } for(i = 0; i<n; i++) os[i] = dis(o,s[i]); for(i = 0; i<(1<<n); i++) dp[i] = -1.0; dfs(i-1); printf("Case #%d: %.2f\n",cas++,dp[i-1]); } return 0; }