题目链接:【HDU 3001】
acmer想要将n个城市全都走遍,每个城市最多走2遍,输入n个城市,编号为1~n,m条路(a,b,c:a,b表示这条路连通的城市,c表示a,b之间的路费),问acmer'走遍这n个城市最少的时间,要是不能走遍n个城市,那就输出-1
遇到这种路的问题,最好将重边考虑进去,免得出错
因为最多能走两次,所以用3进制的状压dp做
城市最多是10个,状态最多是3^10,先预处理出vis[i][j],表示i状态下j出现的次数
dp[i][j]:i表示已走的城市状态,j表示这个状态下j是最后一个城市
dp[i][j] = min(dp[i][j], dp[tmp][k]+dis[k][j]) ==》 tmp是j这个城市少走一次的状态
每次转移时都要判断j k是否在i状态中
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstring>
using namespace std;
int n, m, dis[20][20];
const int inf=1e9;
int vis[60000][15], s[15], dp[60000][15];
void init()
{
for(int i=0; i<=59049; i++)
{
for(int j=1, tmp=i; j<=10; j++, tmp/=3)
{
vis[i][j]=tmp%3;
}
}
s[0]=1;
for(int i=1; i<=10; i++) s[i] = s[i-1]*3;
}
int solve()
{
int ans=1e9;
memset(dp, 0x3f3f3f3f, sizeof(dp));
for(int i=1; i<=10; i++) dp[s[i-1]][i]=0;
for(int i=0; i<s[n]; i++)
{
int flag=1;
for(int j=1; j<=n; j++)
{
if(vis[i][j]==0)
{
flag=0;
continue;
}
for(int k=1; k<=n; k++)
{
if(vis[i][k])
{
int tmp = i-s[j-1];
dp[i][j] = min(dp[i][j], dp[tmp][k]+dis[k][j]);
}
}
}
if(flag)
{
for(int j=1; j<=n; j++)
{
ans = min(ans, dp[i][j]);
}
}
}
return ans==1e9 ? -1:ans;
}
int main()
{
init();
while(~scanf("%d%d", &n, &m))
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
dis[i][j] = inf;
}
}
for(int i=0; i<m; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
dis[a][b] = min(dis[a][b], c);
dis[b][a] = dis[a][b];
}
printf("%d\n", solve());
}
return 0;
}