hlg2130一笔画【状压dp】
一笔画 | ||||||
|
||||||
Description | ||||||
小明喜欢画画,今天他在地上捡起了一张纸,这张纸上有好多个点。小明就想,如果我用铅笔一次性(中间不抬笔)把这些点连起来,那么我的铅笔需要在纸上走多长的路呢?
|
||||||
Input | ||||||
第一行,测试组数t。 每组第一行一个正整数N(1<=N<=15),表示点的个数。 接下来n行,每行两个整数xi,yi(0<=xi,yi<=100),表示点的坐标。
|
||||||
Output | ||||||
求出连接n个点的最短路径长度。保留两位小数。
|
||||||
Sample Input | ||||||
2 3 0 0 0 1 0 2 3 0 0 0 1 1 0
|
||||||
Sample Output | ||||||
2.00 2.00 |
题目描述很简单
我刚开始做这场练习赛的时候没有做出来
后来作者说是状压dp的时候就开始有点思路
现在终于坐上来了
dp[i][j]表示状态为i, j为笔画的最后一个点的最小路径
那么对于一个未加入集合中的点 k
dp[i | ( 1 << ( k - 1) )][k] = min(dp[i | ( 1 << ( k - 1) )][k], dp[i][j] + dist[j][k]);
于是我就想到了一个10^8的算法
每次都枚举一次插入的点
然后TLE了
我们可以想一下‘|’的性质 每一次的‘|’都会是原来的数增大或不变
那么只要依次向后枚举每种状态 那么新状态一定在这个之后
并且该状态一定为最优解
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 7 const int maxn = 20; 8 9 struct Node { 10 double x, y; 11 }node[maxn]; 12 13 double dp[1 << 16][maxn]; 14 15 double dis[maxn][maxn]; 16 bool is[1 << 16][maxn]; 17 18 int xx[1 << 16][maxn]; 19 20 int main() { 21 int M = 1 << 15; 22 memset(is, 0, sizeof(is)); 23 for(int i = 0; i < M; i++) { 24 for(int j = 1; j <= 15; j++) { 25 if(( i & ( 1 << ( j - 1) ) ) != 0) { 26 is[i][j] = true; 27 } 28 } 29 } 30 for(int i = 0; i < M; i++) { 31 for(int j = 1; j <= 15; j++) { 32 xx[i][j] = ( i | ( 1 << ( j - 1) ) ); 33 } 34 } 35 int t, n; 36 scanf("%d",&t); 37 while(t--) { 38 scanf("%d",&n); 39 for(int i = 1; i <= n; i++) { 40 scanf("%lf %lf",&node[i].x, &node[i].y); 41 } 42 for(int i = 1; i <= n; i++) { 43 dis[i][i] = 0; 44 for(int j = i + 1; j <= n; j++) { 45 dis[i][j] = dis[j][i] = sqrt( (node[i].x - node[j].x) * (node[i].x - node[j].x) + (node[i].y - node[j].y) * (node[i].y - node[j].y) ); 46 } 47 } 48 int N = 1 << n; 49 for(int i = 0; i < N; i++) { 50 for(int j = 1; j <= n; j++) { 51 dp[i][j] = 10000.0; 52 } 53 } 54 dp[1][1] = 0; 55 for(int i = 1, j = 1; i < N; i <<= 1, j++) { 56 dp[i][j] = 0; 57 } 58 for(int i = 0; i < N; i++) { 59 for(int j = 1; j <= n; j++) { 60 if(is[i][j]) { 61 for(int k = 1; k <= n; k++) { 62 if(!is[i][k]) { 63 dp[xx[i][k]][k] = min(dp[xx[i][k]][k], dp[i][j] + dis[j][k]); 64 } 65 } 66 } 67 } 68 } 69 double ans = 10000.0; 70 for(int i = 1; i <= n; i++) { 71 ans = min(ans, dp[N - 1][i]); 72 } 73 printf("%.2lf\n", ans); 74 } 75 return 0; 76 }