【上海交大oj】邮递员小F(状态压缩dp)(旅行商问题)

Description

因为制造类专业很难在大城市立足,曾经立志振兴中华之工业的小F,果断在本科毕业后转行做了一名光荣的邮递员。

他的任务是每天从总局出发,行走于所管辖区域的若干的邮局,收集所有的信,然后再汇总返回总局。

因为工作繁忙,同一个邮局他每天只希望去一次。

来往于任意两个邮局是有一定代价的。而且为了方便统计,假定来回两条道路上的代价假设是一样的。

现在小F希望你能给出他每天的最优行走方案,使得总的代价最少。

Input Format

输入数据包括两部分。

第一行为邮局数N。

接下来的N行为一个N×N的对称矩阵。矩阵的第i行,第j列元素Aij代表从邮局i到邮局j的消耗的代价。

规定总局的标号为1。

1N15

0Aij2000

Output Format

共一行,为满足题目要求的最小的代价。

Sample Input

4
0 6 7 9
6 0 6 5
7 6 0 8
9 5 8 0

Sample Output

26


其实就是旅行商问题,用动态规划解决。状态是当前所处的城市位置及已经走过的城市,达到当前状态的路径包括所有已经走过的城市(除了当前城市),需要记录的数据是走过这些城市并且从当前城市回到出发点所需的代价,那么最终需要得到的就是处在n个城市并且走过所有城市的代价中的最小值。保存当前所在城市需要一维数组,但保存走过的城市比较麻烦。由于每个城市都有两种状态(走过没走过),开数组保存就需要一个n维的数组,空间上肯定是不允许的,由于只有两种状态,故可用状态压缩成一个二进制的数来保存走过哪些城市。
具体实现的时候我是用的递归的方法,因为递归比较好理解,不过动态规划一般来说都是用迭代的方法来实现,因为递归调用需要一些时间。不过对于本题,迭代实现会比较麻烦,因为状态比较多,只有一个城市时很好枚举,但当城市数多于3个时,枚举的情况就比较多,可以进行一些预处理,比如把i个城市时的所有状态先保存起来,这样就可以避免写太多的循环。
以下是代码:
 1 #include <iostream>
 2 #include <cmath>
 3 using namespace std;
 4 
 5 int cost[15][15];    //城市间的开销 
 6 int dp[15][32768];  //动归的状态 
 7 int N;
 8 int get_num_of1(int state) //计算一个整数的二进制中有多少个1,相当于状态中走过的城市个数 
 9 {
10     int c = 0;
11     while (state)
12     {
13         state = state&(state-1);
14         c++;
15     }
16     return c;
17 }
18 int getc(int cur,int state)  //递归的过程 
19 {
20     if (dp[cur][state] > -1) return dp[cur][state];
21     //state = ;  //去掉当前位
22     if (get_num_of1(state) == 2) return dp[cur][state] = cost[cur][0];  //没有其他点要经过了
23     int t = 2; //不算出发点
24     int mini = 3000000;
25     int tem;
26     for (int i = 1;i < N;++i,t<<=1)
27     {
28         if (i==cur) continue;
29         if (state & t)  //当前城市走过 
30         {
31             tem = getc(i,state & (~(1<<cur))) + cost[i][cur]; //拿掉当前位
32             if (tem < mini) mini = tem;
33         }
34     }
35     return dp[cur][state] = mini;
36 }
37 
38 int main(){
39     cin>>N;
40     
41     for (int i = 0;i < N;++i) for (int j = 0;j < N;++j) cin>>cost[i][j];
42     int num = pow(2,N);
43     if (N==1)
44     {
45         cout<<0;
46         return 0;
47     }
48     for (int i = 0;i < N;++i) for (int j= 0;j < pow(2,N);++j) dp[i][j] = -1; //初始化 
49     dp[0][1] = 0;
50     int mini = 3000000;
51     for (int i = 1;i < N;++i)  //从第i个城市返回出发点 
52     {
53         int tem = cost[0][i] + getc(i,num-1);/
54         if (tem < mini) mini = tem;
55     }
56     cout<<mini;
57     
58     return 0;
59 }
View Code
原文地址:https://www.cnblogs.com/wenma/p/4646382.html