题目链接:hdu 5067
旅行商问题(TSP)
有一个商人要去n个城镇,给出这n个城镇的坐标(相互的距离),要求出一条每个点经过且只经过一次的最短路径。
状压DP比较适用于n比较小的TSP问题。
用dp【s】【v】表示当前的状态到最后目标位置所需走的最短距离。
s代表当前经过的城镇的集合,若s转化为二进制之后的第i位是1,说明已经走过了i城镇(即状态压缩)
v代表当前所在的城镇标号,仅使用其十进制下的意义,其二进制没有意义。
接下来就可以用记忆化搜索对状态进行dp。
dfs dp代码:
int pin(int s,int v)
{
if(dp[s][v]!=inf)//若此状态之前已经得出,则直接返回
return dp[s][v];
if(s==(1<<st+1)-1)//若所有点已经全部访问过,则根据题目要求(通路、回路)进行下一步返回,这道题是回路
return dp[s][v]=val[v][0];
for(int u=0;u<=st;u++)
{
if(s>>u&1)//若第i位为1,因为不能走回头路,所以继续循环
continue;
dp[s][v]=min(dp[s][v],val[u][v]+pin(s|(1<<u),u));//把问题推给从s|1<<u点走再加上u和v的距离
}
return dp[s][v];
}
从代码里能看出,dp【s】【v】能从dp[s|1<<u][u]中推出,这里注意u∉s,转移到的状态就是dp【{s}U{u}】【u】
AC代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define LL long long
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(a) (a)*(a)
#define lan(a,b) memset(a,b,sizeof(a))
using namespace std;
int a[60][60];
int dp[6010][20];
int val[20][20];
int po[20][3];
int st;
int pou(int x)
{
if(x>0)
return x;
return -x;
}
int pin(int s,int v)
{
if(dp[s][v]!=inf)
return dp[s][v];
//printf("----%d %d\n",s,v);
if(s==(1<<st+1)-1)
return dp[s][v]=val[v][0];
for(int u=0;u<=st;u++)
{
if(s>>u&1)
continue;
// printf("u=%d\n",u);
dp[s][v]=min(dp[s][v],val[u][v]+pin(s|(1<<u),u));
}
// printf("----%d %d\n",s,v);
return dp[s][v];
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
lan(po,0);
lan(val,0);
lan(dp,inf);
lan(a,0);
st=0;
po[0][1]=0;
po[0][2]=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j])
{
if(i==0&&j==0)
continue;
po[++st][1]=i;
po[st][2]=j;
}
}
for(int i=0;i<=st;i++)
for(int j=0;j<=st&&j!=i;j++)
{
val[i][j]=val[j][i]=pou(po[i][1]-po[j][1])+pou(po[i][2]-po[j][2]);
}
printf("%d\n",pin(1,0));
}
return 0;
}