问题描述:
给定一个n*m大小的图(1<=n,m<=50),图上有一些点>0表示该点需要挖坑(点的总数不超过10),现在从起点(1,1)出发,求得只经过所有点一遍再回到起点的最短距离。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5067
-
//poj5067 //状压dp经典问题----TSP问题 // #include<iostream> #include<cmath> #include<cstring> using namespace std; int count; int p[11][2];//所有点的坐标 int dp[1<<11][11];//每种状态下到点i或者说每种状态下从点i开始的最短距离 int dist[11][11];//所有点之间的距离 void init(int pnum) { for(int i=0;i<=pnum;i++) { for(int j=1;j<=pnum;j++) { if(i==0)//从起点(1,1)遍历到的点的距离(可以直接-2计算) { dist[j][i]=dist[i][j]=p[j][0]+p[j][1]-2; } else//其余的点之间的距离都可以等于(横坐标之差+纵坐标之差),其实从起点开始的也可以直接写这个公式 { dist[j][i]=dist[i][j]=abs(p[j][0]-p[i][0])+abs(p[j][1]-p[i][1]); } } } } int main() { int n,m; while(cin>>n>>m) { memset(dist,0,sizeof dist);//初始化所有点之间的距离 memset(dp,0,sizeof dp);//初始化状压数组 int pnum=0;//表示记录点的数量 for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int num; cin>>num; if(num>0) { //把每个需要挖掘的点存入数组p中(起点(1,1)除外) if(i==1&&j==1)continue; pnum++; p[pnum][0]=i;//横坐标存起来 p[pnum][1]=j;//纵坐标存起来 } } } init(pnum);//计算所有挖掘点之间的距离 int statenum=1<<pnum;//确定状压的范围(这里点的个数最多也就10个,2的10次方=1024完全可以装的下,也跑得完) for(int s=0;s<=statenum;s++)//开始遍历每种情况 { for(int i=1;i<=pnum;i++)//求每种情况与点i的关系,用1的(i-1)进制与s进行位与,求得在此情况下,点i是否被选中 { int tmp=1<<(i-1); if(s&tmp)//如果被选中 { if(s==tmp)//直接判断这种情况是不是只有点i被选中(节省循环时间) { dp[s][i]=dist[0][i];//dp表示该状态下到达某点i的最短距离(也可以说是从点i开始的) } else { dp[s][i]=0x3f3f3f3f;//(有好多点被选中的那种情况,所以先将dp值最大化,然后一步一步取小) for(int k=1;k<=pnum;k++)//再次遍历这pnum个点,计算其他状态下到达k点+k点到达点i的距离,一步一步取小dp值 { if(s&(1<<(k-1))&&k!=i) { dp[s][i]=min(dp[s][i],dp[s^(1<<(i-1))][k]+dist[k][i]);//s异或1<<(i-1)的意义就是传入从其他状态下(没有到达过点i的状态)到达点k的距离,再加上一个点k到达点i的距离,就是一个s状态下起点到达点i的新距离,取小 } } } } } } int ans=0x3f3f3f3f;//先将答案最大化 for(int i=1;i<=pnum;i++) { ans=min(ans,dp[statenum-1][i]+dist[0][i]);//取所有点都遍历的状态下(除起点外)到达点i的距离+起点到达点i的距离 //取小 } if(ans==0x3f3f3f3f)ans=0;//如果还等于一开始最大化的值,说明没有所有只走一遍的情况 cout<<ans<<endl; } return 0; }
后续会补上TSP相关的总结