题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=5067
★这是我做的第一道TSP题,TSP 即旅行商问题,简单来说就是有一个起点s,有n个要去的点,求 每个点都到达过且每个点只能到一次 最终回到起点s 所走过的最短路径 n比较少时,用状压DP即可~
翻译:
有一个n * m的区域,起点在左上角(1,1)点,n * m个点中,输入n * m的矩阵,大于0的点就是要到达的点。求 到达每个点一次 且最终回到原点 所经过的最短路程 即简单TSP问题
思路:
题目说了 要到达的点不超过 10个,那么当然可以用 状压DP解决~
①:找到这cnt个目的点,并求出他们各自之间的距离(横坐标之差的绝对值 加 纵坐标之差的绝对值)
②:dp[ i ] [ j ] 表示 状态 i 的最后一个点是 j 时的最短路程 (这时没有算回到原点的路程,最后加上就行)
③:状态转移方程:dp[i][j]=min(dp[i][j],dp[i^tmp][k]+dis[k][j]);
(dis数组表示两点间的距离)
④:注意最后要小心 ans=inf (即一个目的点都没有的情况 我被这里卡了) 要ans=0处理
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<string>
using namespace std;
const int maxn=12;
const int sz=1<<11;
const int mod=1e9+7;
const int inf=2e9+7;
const double pi=3.1415926;
typedef long long LL;
int n,m;
struct node
{
int x,y;
}f[maxn];
int dp[sz][maxn];
int dis[maxn][maxn];
template<class T>
inline void read(T &x)
{
char c; x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int x;
read(x);
if(x){
f[++cnt].x=i;
f[cnt].y=j;
}
}
}
for(int i=1;i<=cnt;i++){ //目的点之间的距离
for(int j=1;j<=cnt;j++){
dis[i][j]=abs(f[i].x-f[j].x)+abs(f[i].y-f[j].y);
}
}
int num=1<<cnt;
memset(dp,0,sizeof dp);
for(int i=0;i<num;i++){ //TSP
for(int j=1;j<=cnt;j++){
int tmp=1<<j-1;
if(i&tmp){
if(i==tmp) dp[i][j]=f[j].x+f[j].y-2; //到原点的距离 后面也用到了
else{
dp[i][j]=inf;
for(int k=1;k<=cnt;k++){
if((i&(1<<k-1))&&k!=j){
dp[i][j]=min(dp[i][j],dp[i^tmp][k]+dis[k][j]);
}
}
}
}
}
}
int ans=inf;
for(int i=1;i<=cnt;i++){
ans=min(ans,dp[num-1][i]+f[i].x+f[i].y-2); //加上 到原点的距离
}
if(ans==inf) ans=0;
cout<<ans<<endl;
}
return 0;
}