题意:
给一个n点m边的无向图,求经过所有点,并且经过每个点不超过两次的最短路长度;
数据:
input:
5 6
1 2 1
2 3 1
1 3 1
1 5 1
1 4 100
5 4 10
output:
13
题解:
一开始不认真看英文就以为是很简单的状压TSP问题,并且样例也过了。。;
然而是有坑的,所有并不简单;
依然是考虑状态f[i][j],在第i个点,经过点状态为j的最短路长度;
但是为了表示经过每个点几次,j是用三进制压缩;
于是转移大概就是f[i][j]=min(f[x][k]) 1<=x<=n,k为j状态去掉一个i的前一个状态;
然后写好进制转换就好了(反正我写错好几次= =);
代码略丑(笑);
2015-6-29:
白书告诉我们,遇到三进制就维护四进制就好。。。
真是有道理啊,然而懒得改就放在这提醒后人(?)吧。。。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 11
#define INF 0x3f3f3f3f
using namespace std;
int map[N][N],f[N][100000];
void init()
{
memset(map,0x3f,sizeof(map));
memset(f,0x3f,sizeof(f));
}
int AN(int k,int x)<span style="white-space:pre"> </span>//判断状态中是否经过某个点
{
for(int i=1;i<x;i++)
{
k/=3;
}
return k%3;
}
int now(int k,int x)<span style="white-space:pre"> </span>//求上一个状态
{
int i,temp=0;
for(i=1;i<x;i++)
{
temp*=3;
temp+=k%3;
k/=3;
}
k--;
for(i=1;i<x;i++)
{
k*=3;
k+=temp%3;
temp/=3;
}
return k;
}
bool judge(int k,int n)<span style="white-space:pre"> </span>//判断是否满足每个点都经过
{
for(int i=1;i<=n;i++)
{
if(k%3==0) return 0;
else k/=3;
}
return 1;
}
int main()
{
int n,m,i,j,k,x,y,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&v);
map[x][y]=min(map[x][y],v);
map[y][x]=min(map[y][x],v);
}
for(i=1,v=1;i<=n;i++,v*=3) map[i][i]=0,f[i][v]=0;
for(k=0,x=INF;k<v;k++)
{
for(i=1;i<=n;i++)
{
if(AN(k,i))
{
for(j=1,y=now(k,i);j<=n;j++)
{
if(map[i][j]!=INF&&AN(y,j))
{
f[i][k]=min(f[j][y]+map[i][j],f[i][k]);
}
}
}
}
if(judge(k,n))
{
for(i=1;i<=n;i++)
{
x=min(x,f[i][k]);
}
}
}
if(x!=INF)
printf("%d\n",x);
else
printf("-1\n");
}
}