题目链接:点击打开链接
题意:
给定n个点m条边的无向图
求从1点开始经过每条边至少一次最后回到1点的最小路程
显然就是找一条路径可重复的欧拉回路
思路:
首先对于欧拉回路的结论是:所有点的度数都为偶数
因为所有边至少经过一次,那么可以把题意转换成加最少多少条边使得图满足以上结论
而加的边目的是为了把奇度数转成偶度数,先floyd一下得到任意点间加边的最小花费
dp[i]表示状态i下度数都为偶数的最小花费。
状压dp,把i状态下,所有未选择的点中挑2个奇度数的转移即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <set>
#include <math.h>
using namespace std;
#define ll int
#define inf 1000000007
#define N 16
int dis[N][N], n, m;
void floyd(){
for(ll k = 0; k < n; k++)
for(ll i = 0; i < n; i++)
for(ll j = 0; j < n; j++)
dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
for(ll i = 0; i < n; i++)dis[i][i] = 0;
}
int du[N], ans, dp[1<<N];//dp[i] 表示加边使得i状态下的所有点都是偶度数的最小花费
ll work(){
ll i, j, k;
for(i = 1; i < n; i++)//如果i这个点有边连接,但点0又走不到这个点,说明和这个点相连的边是走不到的,即图不连通
if(du[i] && dis[0][i]==inf)return -1;
ll all = (1<<n)-1;
for(i = 0; i <= all; i++) dp[i] = inf;
dp[0] = 0;
for(k = 0; k <= all; k++)
{
for(i = 0; i < n; i++)//找到一个奇度数的点i
if((du[i]&1) && (k&(1<<i)))break;
if(i==n)dp[k] = 0;
for(i = 0; i < n; i++)// 枚举奇数度点i
if((du[i]&1) && !(k&(1<<i)))
for(j = i+1; j < n; j++)// 枚举奇数度点j
if((du[j]&1) && !(k&(1<<j)) && dis[i][j]<inf)
dp[k|(1<<i)|(1<<j)] = min(dp[k|(1<<i)|(1<<j)],dp[k]+dis[i][j]);
}
if(dp[all]>=inf)return -1;
return ans + dp[all];
}
int main(){
ll i, j, u, v, w;
while(cin>>n>>m){
for(i=0;i<n;i++)for(j=0;j<n;j++)dis[i][j] = inf;
memset(du, 0, sizeof du);
ans = 0;
while(m--){
cin>>u>>v>>w; u--; v--;
dis[u][v] = dis[v][u] = min(dis[u][v], w);
du[u]++; du[v]++;
ans += w;
}
floyd();
cout<<work()<<endl;
}
return 0;
}