http://acm.hdu.edu.cn/showproblem.php?pid=3001
题意:
n
个
点
m
条
边
,
每
条
边
有
个
费
用
,
可
以
选
择
任
意
起
点
和
终
点
,
求
最
多
经
过
每
个
点
两
次
,
并
且
遍
历
每
个
点
的
最
小
费
用
n个点m条边,每条边有个费用,可以选择任意起点和终点,求最多经过每个点两次,并且遍历每个点的最小费用
n个点m条边,每条边有个费用,可以选择任意起点和终点,求最多经过每个点两次,并且遍历每个点的最小费用
题解:
经
过
每
个
点
至
多
两
次
,
考
虑
状
压
d
p
经过每个点至多两次,考虑状压dp
经过每个点至多两次,考虑状压dp
计
算
三
进
制
状
态
,
其
中
0
,
1
,
2
表
示
不
经
过
,
经
过
1
次
,
经
过
2
次
计算三进制状态,其中0,1,2表示不经过,经过1次,经过2次
计算三进制状态,其中0,1,2表示不经过,经过1次,经过2次
状
态
转
移
方
程
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
3
j
]
[
k
]
+
d
[
k
]
[
j
]
)
状态转移方程 dp[i][j] = min(dp[i][j],dp[i-3^j][k]+d[k][j])
状态转移方程dp[i][j]=min(dp[i][j],dp[i−3j][k]+d[k][j])
i
−
3
j
表
示
状
态
i
第
j
位
减
一
(
如
果
第
j
位
为
0
就
变
成
2
)
,
转
移
方
程
就
是
:
第
j
位
没
经
过
的
地
方
转
移
到
第
j
位
i-3^j 表示状态i第j位减一(如果第j位为0就变成2),转移方程就是:第j位没经过的地方转移到第j位
i−3j表示状态i第j位减一(如果第j位为0就变成2),转移方程就是:第j位没经过的地方转移到第j位
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <string>
using namespace std;
typedef long long ll;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&-x
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const double pi = acos(-1.0);
int n,m,u,v,w;
int fac[15],d[15][15],dig[maxn][15];
ll dp[maxn][15]; //dp[i][j] 对于状态i,最后到达j点的最短路
void init(){
fac[0] = 1;
for(int i = 1; i <= 10; i++){
fac[i] = fac[i-1]*3;
}
for(int i = 0; i < fac[10]; i++){
int res = i;
for(int j = 0; j < 10; j++){
dig[i][j] = res%3;
res /= 3;
}
}
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
init();
while(~scanf("%d%d",&n,&m)){
mem(d,inf);
mem(dp,inf);
for(int i = 1; i <= m; i++){
scanf("%d%d%d",&u,&v,&w);
u--,v--;
d[u][v] = d[v][u] = min(w,d[u][v]);
}
for(int i = 0; i < n; i++){
dp[fac[i]][i] = 0; //初始化,以i为终点的最小花费为0
}
ll ans = inf;
for(int i = 0; i < fac[n]; i++){
int ok = 1; //状态i是否经过所有点
for(int j = 0; j < n; j++){ //终点
if(dig[i][j] == 0){ //状态i第j位为0表示这个点没经过,j不能作为终点
ok = 0;
continue;
}
for(int k = 0; k < n; k++){ //中转点
if(dig[i][k] == 0) continue;
dp[i][j] = min(dp[i][j],dp[i-fac[j]][k]+d[k][j]);
}
}
if(ok){
for(int j = 0; j < n; j++)
ans = min(ans,dp[i][j]);
}
}
if(ans == inf) puts("-1");
else
printf("%lld\n",ans);
}
return 0;
}