集合上的动态规划--最优配对问题

 转自此博客

题目:刘汝佳《算法竞赛入门经典》,集合上的动态规划---最优配对问题 题意:空间里有n个点P0,P1,...,Pn-1,你的任务是把它们配成n/2对(n是偶数),使得每个点恰好在一个点对中。所有点对中两点的距离之和应尽量小。状态:d(i,S)表示把前i个点中,位于集合S中的元素两两配对的最小距离和 状态转移方程为:d(i,S)=min{|PiPj|+d(i-1,S-{i}-{j}} 

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <math.h>
 5 #include <algorithm>
 6 using namespace std;
 7 const int N = 20;
 8 const int INF = 0x3f3f3f3f;
 9 double d[N + 5][(1 << N) + 10];
10 struct Node
11 {
12     int x, y, z;
13 }node[N + 10];
14 double dist(int n1, int n2)
15 {
16     return sqrt( double( (node[n1].x - node[n2].x) * (node[n1].x - node[n2].x) + (node[n1].y - node[n2].y) * (node[n1].y - node[n2].y) + (node[n1].z - node[n2].z) * (node[n1].z - node[n2].z)));
17 }
18 int main()
19 {
20     int n;
21     scanf("%d", &n);
22     for(int i = 1; i <= n; i++)
23     {
24         scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].z);
25     }
26     for(int i = 0; i < n; i++)
27     {
28         for(int S = 0; S < (1 << (i + 1) ); S++)
29         {
30             if(S == 0)
31                 d[i][S] = 0;
32             else
33                 d[i][S] = INF;
34             if(S & (1 << i))
35             {
36                 for(int j = 0; j < i; j++)
37                 {
38                     if(S & (1 << j) )
39                         d[i][S] = min(d[i][S], dist(i, j) + d[i - 1][S ^ (1 << i) ^ (1 << j)]);
40                 }
41             }
42             else if(i != 0)
43             {
44                 d[i][S] = d[i - 1][S];
45             }
46         }
47     }
48     printf("%.3lf\n", d[n - 1][(1 << n) - 1]);
49     return 0;
50 }
View Code

 

状态可以进行压缩,i的值其实隐藏在S中,S中最高位为1的即为i,所以需要一次查找,从n-1到0进行一次历编即可,整个运算下来,平均查找次数仅为2。而且方法二比方法一情况简单很多,也比较容易理解。 

 1 #include <cstring>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <iostream>
 6 using namespace std;
 7 const int N = 20;
 8 const int INF = 0x3f3f3f3f;
 9 double d[(1 << N) + 10];
10 struct Node
11 {
12     int x, y, z;
13 }node[N + 10];
14 int n, S;
15 double dist(int n1, int n2)
16 {
17     return sqrt( double( (node[n1].x - node[n2].x) * (node[n1].x - node[n2].x) + (node[n1].y - node[n2].y) * (node[n1].y - node[n2].y) + (node[n1].z - node[n2].z) * (node[n1].z - node[n2].z)));
18 }
19 void input()
20 {
21     for(int i = 0; i < n; i++)
22     {
23         scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].z);
24     }
25     S = 1 << n;
26     d[0] = 0;
27 }
28 void solve()  
29 {
30     for(int s = 1; s < S; s++)
31     {
32         int i, j;
33         d[s] = INF;
34         for(i = n - 1; i >= 0; i--)  //d[s]就是前i个位于集合s中距离最小,只要找到i,再从i往前找j不就行了。
35             if(s & (1 << i))
36                 break;
37         for(j = i - 1; j >= 0; j--)
38             if(s & (1 << j))
39                 d[s] = min(d[s], dist(i, j) + d[s ^ (1 << i) ^ (1 << j)]);
40     }
41 }
42 int main()
43 {
44     scanf("%d", &n);
45     input();
46     solve();
47     printf("%0.3lf\n", d[S - 1]);
48     return 0;
49 }
View Code

 

这道题用递归实现更好一些,因为只需要判断n为偶数的情况,这就是递归运算的好处,而非递归则需要全部都进行一次运算。 

 1 #include <cstring>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cmath>
 5 #include <iostream>
 6 using namespace std;
 7 const int N = 20;
 8 const int INF = 0x3f3f3f3f;
 9 double d[(1 << N) + 10];
10 struct Node
11 {
12     int x, y, z;
13 }node[N + 10];
14 int n, S;
15 double dist(int n1, int n2)
16 {
17     return sqrt( double( (node[n1].x - node[n2].x) * (node[n1].x - node[n2].x) + (node[n1].y - node[n2].y) * (node[n1].y - node[n2].y) + (node[n1].z - node[n2].z) * (node[n1].z - node[n2].z)));
18 }
19 void input()
20 {
21     scanf("%d", &n);
22     for(int i = 0; i < n; i++)
23     {
24         scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].z);
25     }
26     S = 1 << n;
27     //memset(d, INF, sizeof(d)); 慎用emset()
28     for(int i = 0; i < S; i++)
29         d[i] = INF;
30     d[0] = 0;
31 
32 }
33 double dp(int s)
34 {
35     if(d[s] != INF)
36         return d[s];
37     int i, j;
38     for(i = n - 1; i >= 0; i--)
39         if(s & (1 << i))
40             break;
41     for(j = i - 1; j >= 0; j--)
42         if(s & (1 << j))
43             d[s] = min(d[s], dp(s ^ (1 << i) ^ (1 << j)) + dist(i, j));
44     return d[s];
45 }
46 int main()
47 {
48     input();
49     printf("%0.3lf\n", dp(S - 1));
50     return 0;
51 }
View Code

 

posted @ 2016-03-16 11:26  zhaop  阅读(620)  评论(0编辑  收藏  举报