题解 P1171 【售货员的难题】
TSP问题,也算是状压DP经典吧
我们以一串二进制数表示村庄的集合(状态)
1表示该村庄访问过,0表示没有
其中map数组是题目给定的各村庄间距离
而求取dp数组的方法 我们可以借鉴Floyd的思想
具体代码:
for(int i=0;i<=(1<<n)-1;++i)
for(int j=1;j<=n;++j)
if( ( (1<<j-1)&i )==0 )
for(int k=1;k<=n;++k)
if( ( (1<<k-1)&i) )
dp[i|(1<<j-1)][j]=min(dp[i|(1<<j-1)][j],dp[i][k] + rem[k][j]);
如何解释呢
第一层循环 i 枚举每个状态
第二层循环 j 枚举下一步到达的点
if( !( (1 << j-1) & i) ) 这句判断 j 是否已访问
1左移j-1位,则此时只有第j位是1
若状态 i 的第 j 位为1,则&与运算返回1,表示已访问,否则没访问
第三层循环枚举中介点k, 其中if语句判断同上
搜索具体代码,应该不难理解
void dfs(int x,int u)//x是当前状态,u是当前到达的结点
{
for(int i=1;i<=n;++i)
if(((1<<i-1)&x)==0)//枚举还未到达的点
if(dp[x|(1<<i-1)][i]>dp[x][u]+rem[u][i])//如果可以更新就继续搜索
{
dp[x|(1<<i-1)][i]=dp[x][u]+rem[u][i];
dfs(x|(1<<i-1),i);
}
}
最终答案判断方法与上述DP相同
虽然这种方法艹不过此题,但建议要理解这种思路
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef double dd;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=1050000;
int n,ans=2e9;
int rem[22][22],dp[maxn][22];
void dfs(int x,int u)
{
for(int i=1;i<=n;++i)
if(((1<<i-1)&x)==0)
if(dp[x|(1<<i-1)][i]>dp[x][u]+rem[u][i])
{
dp[x|(1<<i-1)][i]=dp[x][u]+rem[u][i];
dfs(x|(1<<i-1),i);
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
rem[i][j]=read();
memset(dp,67,sizeof(dp)); dp[1][1]=0;
dfs(1,1);
for(int i=1;i<=n;++i)
ans=min(ans,dp[(1<<n)-1][i]+rem[i][1]);
printf("%d",ans);
return 0;
}