一道状态压缩dp。每个点最多经过两次,求遍历所有点的最小费用。
点的数量最多只有10个,这么小的数据让人很容易就想到了状态dp。最初用dp[10][1<<21],就是将点数变成20个,来判断某点是否已经经过两次。很明显超空间了。 - .-
由于每个点有3个状态(没去过,去过1次,去过2次),所以改成3进制,dp[10][3^10](每个点最多去两次3^10够了)。
为了节省时间,关于3进制的操作需要进行预处理(见代码Init()部分)。剩下的dp就没什么技术含量了。
ps:题目没有保证没重边,没有到自己的边。。。 = =|||
#include<stdio.h>
#include<iostream>
#include<memory.h>
using namespace std;
int n,m,ans;
int Map[10][10],dp[10][60000],Hash[10][60000],Max[11];
void Init();
bool check(int k);
int main()
{
Init();
int a,b,tmp;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(dp,-1,sizeof(dp));
memset(Map,-1,sizeof(Map));
ans=-1;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&tmp);
a--,b--;
if(a==b)
continue;
if(Map[a][b]!=-1)
Map[a][b]=Map[b][a]=min(tmp,Map[a][b]);
else
Map[a][b]=Map[b][a]=tmp;
}
tmp=1;
for(int i=0;i<n;i++)
{
dp[i][tmp]=0;
tmp*=3;
}
for(int Set=1;Set<Max[n];Set++)
{
for(int i=0;i<n;i++)
{
if(dp[i][Set]==-1)
continue;
for(int j=0;j<n;j++)
{
if(Map[i][j]==-1 || Hash[j][Set]==2)
continue;
int neset=0;
for(int k=n-1;k>=0;k--)
{
if(j==k)
neset=neset*3+Hash[k][Set]+1;
else
neset=neset*3+Hash[k][Set];
}
tmp=dp[i][Set]+Map[i][j];
if(tmp<dp[j][neset] || dp[j][neset]==-1)
dp[j][neset]=tmp;
}
}
}
for(int i=1;i<Max[n];i++)
{
for(int j=0;j<n;j++)
{
if(check(i) && dp[j][i]!=-1)
{
if(dp[j][i]<ans || ans==-1)
ans=dp[j][i];
}
}
}
printf("%d\n",ans);
}
return 0;
}
void Init()
{
int r=3;
for(int i=1;i<=10;i++)
{
Max[i]=r-1;
r*=3;
}
for(int i=1;i<Max[10];i++)
{
r=i;
for(int j=0;j<10;j++)
{
Hash[j][i]=r%3;
r/=3;
}
}
}
bool check(int k)
{
for(int i=0;i<n;i++)
{
if(Hash[i][k] == 0)
{
return false;
}
}
return true;
}