codeforces 8c Looking for Order
https://vjudge.net/problem/CodeForces-8C
题意:
一个平面上放着许多东西,每个东西都有一个坐标,最开始一个人在一个起始坐标,她出发去拿东西,一次要么拿一件东西,要么拿两件东西,拿了之后必须返回起始坐标。
每次花费的时间是两个坐标距离的平方,问拿完所有的东西需要的最少的时间。
思路:
由于数据范围比较小,所以可以考虑用状压dp来写。由于每次拿东西之后都要返回起点,那么其实拿东西的顺序是没有影响的,所以利用题目给定的顺序进行剪枝,即每次进行扩展的时候都考虑在前面的点已经取完了。
然后每次记录的时候,非常巧妙的方法,如果一个点的话,直接记录这个点,如果有两个点的话,那么就用(i+1)*100 + (j+1)来记录,因为点数最多时只有24,输出的时候递归输出就行了,递归输出这里还是比较巧妙的。
代码:(有详细的注释)
1 #include <stdio.h> 2 #include <string.h> 3 #include <vector> 4 using namespace std; 5 6 struct node 7 { 8 int x,y; 9 } a[30]; 10 11 int mp[30][30]; 12 vector<int> ans; 13 14 int cal(int i,int j) 15 { 16 return (a[i].x - a[j].x) * (a[i].x - a[j].x) + (a[i].y - a[j].y) * (a[i].y - a[j].y); 17 } 18 19 const int maxn = (1 << 24)+2; 20 const int gg = 0x3f3f3f3f; 21 22 int dp[maxn],last[maxn],rec[maxn]; 23 24 void output(int s) 25 { 26 if (~last[s]) 27 output(last[s]);//递归输出 28 29 if (rec[s]) 30 { 31 if (rec[s] > 100) ans.push_back(rec[s] / 100);//第一个扩展的点 32 33 ans.push_back(rec[s] % 100); 34 ans.push_back(0);//每次都要返回起点 35 } 36 } 37 38 int main() 39 { 40 int n; 41 42 node tmp; 43 44 scanf("%d%d",&tmp.x,&tmp.y); 45 46 scanf("%d",&n); 47 48 for (int i = 0;i < n;i++) 49 { 50 scanf("%d%d",&a[i].x,&a[i].y); 51 } 52 53 a[n] = tmp; 54 55 for (int i = 0;i <= n;i++) 56 for (int j = i + 1;j <= n;j++) 57 mp[i][j] = mp[j][i] = cal(i,j); 58 59 memset(dp,gg,sizeof(dp)); 60 memset(last,-1,sizeof(last)); 61 62 dp[0] = 0; 63 64 for (int s = 0;s < (1<<n);s++) 65 { 66 if (dp[s] >= gg) continue; 67 68 for (int i = 0;i < n;i++) 69 { 70 if (((1 << i) & s) == 0)//这个点没有被走过 71 { 72 int val = dp[s] + mp[n][i] * 2; 73 74 if (dp[s|(1 << i)] > val) 75 { 76 dp[s|(1 << i)] = val; 77 last[s|(1 << i)] = s;//记录前驱,下同 78 rec[s|(1 << i)] = i+1; 79 } 80 81 for (int j = i + 1;j < n;j++)//从i+1开始枚举保证了不会有重复情况 82 { 83 if (((1<<j)&s) == 0) 84 { 85 int tmp = dp[s] + mp[n][i] + mp[i][j] + mp[j][n]; 86 87 if (dp[s|(1<<i)|(1<<j)] > tmp) 88 { 89 dp[s|(1<<i)|(1<<j)] = tmp; 90 last[s|(1<<i)|(1<<j)] = s; 91 rec[s|(1<<i)|(1<<j)] = (i+1) * 100 + (j+1);//巧妙的记录 92 } 93 } 94 } 95 96 break;//强行顺序剪枝 97 } 98 } 99 100 //printf("%d\n",dp[s]); 101 } 102 103 104 ans.clear(); 105 106 output((1<<n)-1); 107 108 printf("%d\n",dp[(1<<n)-1]); 109 110 printf("0 "); 111 112 for (int i = 0;i < ans.size();i++) 113 printf("%d%s",ans[i],i == ans.size() - 1 ? "\n" :" "); 114 115 return 0; 116 }
康复训练中~欢迎交流!