题目链接:HDU3001 Travelling
题意:n个点m条边的无向图,问每个点至少访问一次至多访问两次的最小花费是多少;
分析:三进制状压DP,每一个点有访问0、1、2次三种状态,那么就一共有3^(n-1)-1种状态;因为不是二进制,不能进行位运算,所以要提前预处理出所有状态三进制下每一位的值,然后就是枚举所有状态的每一位,向其他状态进行转移;
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int p[15],num[60000][12],dp[60000][12],mp[12][12];
void init()
{
p[0]=1;
for(int i=1;i<=10;i++) p[i]=p[i-1]*3;
for(int i=1;i<=p[10];i++)
{
int x=i;
for(int j=0;j<=10;j++) num[i][j]=x%3,x/=3;
}
}
void rua()
{
int ans=INF;
memset(mp,INF,sizeof(mp));
for(int i=1;i<=m;i++)
{
int a,b,c;scanf("%d%d%d",&a,&b,&c);
mp[a-1][b-1]=mp[b-1][a-1]=min(mp[a-1][b-1],c);
}
memset(dp,INF,sizeof(dp));
for(int i=0;i<n;i++) dp[p[i]][i]=0;
for(int i=0;i<p[n];i++)
{
bool flag=true;
for(int j=0;j<n;j++)
{
if(num[i][j]==0) flag=false;
if(dp[i][j]==INF) continue;//这个位置无法向后更新
for(int k=0;k<n;k++)
{
if(num[i][k]>=2 || k==j || mp[j][k]==INF) continue;
dp[i+p[k]][k]=min(dp[i+p[k]][k],dp[i][j]+mp[j][k]);
}
}
if(flag) for(int j=0;j<n;j++) ans=min(ans,dp[i][j]);
}
if(ans==INF) ans=-1;
printf("%d\n",ans);
return ;
}
int main()
{
init();
while(~scanf("%d%d",&n,&m)) rua();
return 0;
}