1.因为每个城市最多访问2次,所以用状态0,1,2记录某个城市被访问的次数,把1~N个城市的访问状态连在一起可以用一个三进制数记录。
2.定义DP[i][j]中i为一个三进制数表示N个城市的访问情况,然后j表示目前访问到的城市序号,那么我们可以得出状态转移方程:
dp[i+3v][v]=min(dp[i+3v][v],dp[i][j]+Cjv)
表示从城市j出发到城市v的情况最小花费。
- AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define gets(i,j) (i/len[j])%3 //用于计算压缩状态i下城市j的状态
using namespace std;
int dp[60000][11];
int c[20][20];
int len[11];
int n,m;
int main()
{
len[0] = 1;
for(int i=1;i<=10;i++)
len[i] = len[i-1]*3;
int n,m,f,u,v,a,b,w,ans;
while(~scanf("%d%d", &n, &m))
{
memset(c, 0x3f3f3f3f, sizeof(c)); //初始化任意城市间道路距离为无穷
memset(dp, 0x3f3f3f3f, sizeof(dp)); //初始化所有状态为无穷,表示该状态还未到达
for(int i=0;i<m;i++)
{
scanf("%d%d%d", &a, &b, &w);
a--,b--; //将城市编号转换成从0开始到n-1
c[a][b] = c[b][a] = min(c[a][b], w); //a,b间距离取所有道路的最小值
}
ans = dp[0][0]; //最小费用赋值为无穷
for(int i=0;i<n;i++)
dp[len[i]][i] = 0; //初始化每个城市作为出发点的状态
for(int i=1;i<len[n];i++)
{
f=1;
for(int j=0;j<n;j++)
{
if(gets(i,j) == 0)
{
f = 0;
continue;
} //在状态i下,最后到达城市为j,转移到其他状态
for(int v=0;v<n;v++)
{ //表示城市v在状态i下已经访问过两次,不能再次访问
if(gets(i,v) == 2)
continue;
u = i +len[v];
dp[u][v] = min(dp[u][v], dp[i][j]+c[j][v]);
}
}
if(f)
for(int j=0;j<n;j++)
ans=min(ans, dp[i][j]);
}
if(dp[0][0] == ans)
ans = -1;
printf("%d\n", ans);
}
return 0;
}