CodeForces 21D 题解
题解笔记
CF21D Traveling Graph(Traveling.cpp)
时间限制 \(0.5s\) | 内存限制 \(64M\)
题目描述:
给定一个有 \(n\) 个顶点(编号从 \(1\) 到 \(n\)),\(m\) 条带权边的无向图,要求从顶点 \(1\) 开始经过每一条边至少一次最后回到起点 \(1\),要求走过的边权值总和最小。注意:可能有重边和自环。
输入格式:
第一行 \(n\),\(m\) ( \(1 \leq n \leq 15\) ,\(0 \leq m \leq 2000\)),接下来的 \(m\) 行,每行包含三个数 \(u,v,w\) (\(1 \leq w \leq 104\)),描述一条顶点 \(u\) 和顶点 \(v\) 之间有一条权值为 \(w\) 的无向边。
输出格式:
仅有一个整数 \(c\) ,表示最小的边权和,若无解则输出 −1 。
输入输出样例:
样例1输入 | 样例1输出 | 样例2输入 | 样例2输出 |
---|---|---|---|
3 3 1 2 1 2 3 1 3 1 1 |
3 | 3 2 1 2 3 2 3 4 |
14 |
解题思路:
按照CodeForces上的标签,这道题的算法应该是图论和图匹配,尝试了一下,猜测大概正解应该是欧拉回路+匈牙利算法,但其实欧拉回路+暴力枚举优化完全可以过了。
我们只需要考虑重复走的路程就好了,答案就是重复走的路程+边权值之和
首先要先知道欧拉回路以及欧拉图
通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。
具有欧拉回路的无向图或有向图称为欧拉图。
对于无向图 \(G\) , \(G\) 是欧拉图当且仅当 \(G\) 是连通的且没有奇度顶点。
考虑一下重复走的路程的情况,就是当图是欧拉图时,完全不重复走
于是,那重复走其实就是当你多走了这么多路后,图就变成了欧拉图。
很类似小奥一笔画问题中,将一个图添了多少笔后,就可以一笔画了
所以,接下来的任务就是消除所有入度为奇数的点
于是,就计算一遍全源最短路
然后暴力枚举,将入度为奇数的点两两配对,枚举所有的方案,取连边的边权值之和最小的方案,输出连边的边权值之和+边权值之和就好了(记得要判 \(-1\) 的情况,我用的是并查集)
关于暴力
以上代码实现步骤里,其实我个人感觉暴力是很重要的,因为我大部分时间都在优化暴力
暴力枚举 \(1.0\):
void dfs(int r,int cnt)
{
if(r==tot>>1) ans=min(ans,cnt);
else
{
for(int i=1; i<tot; i++)
{
if(!vis[i])
{
for(int j=i+1; j<=tot; j++)
{
if(!vis[j])
{
vis[i]=vis[j]=1;
dfs(r+1,cnt+dis[od[i]][od[j]]);
vis[i]=vis[j]=0;
}
}
}
}
}
}
这个版本是 TLE
了的,时间复杂度应该是接近\(O(n^n)\) ,于是我就发现了可以优化 \(i\) 的初始值,将上一个 \(i\) 的初始值记录下来,这一次的 \(i\) 就从上一个的 \(i\) 加 \(1\) 开始枚举,然后就可以优化为 \(O(n!)\) 的时间复杂度
暴力枚举 \(2.0\):
void dfs(int r,int cnt,int lmt)
{
if(r==tot>>1) ans=min(ans,cnt);
else
{
for(int i=lmt; i<tot; i++)
{
if(!vis[i])
{
for(int j=i+1; j<=tot; j++)
{
if(!vis[j])
{
vis[i]=vis[j]=1;
dfs(r+1,cnt+dis[od[i]][od[j]],i+1);
vis[i]=vis[j]=0;
}
}
}
}
}
}
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=17;
int dis[MAXN][MAXN],sum,deg[MAXN],ans=0x3f3f3f3f,od[MAXN],tot,vis[MAXN];
int beg[2005],en[2005];
void dfs(int r,int cnt,int lmt)
{
if(r==tot>>1) ans=min(ans,cnt);
else
{
for(int i=lmt; i<tot; i++)
{
if(!vis[i])
{
for(int j=i+1; j<=tot; j++)
{
if(!vis[j])
{
vis[i]=vis[j]=1;
dfs(r+1,cnt+dis[od[i]][od[j]],i+1);
vis[i]=vis[j]=0;
}
}
}
}
}
}
int fa[MAXN];
int ufind(int x)
{
while(x!=fa[x]) x=fa[x]=fa[fa[x]];
return x;
}
int main()
{
memset(dis,0x3f,sizeof dis);
int n,m;
scanf("%d%d",&n,&m);
if(m==0)
{
printf("0\n");
return 0;
}
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1; i<=m; i++)
{
int w;
scanf("%d%d%d",&beg[i],&en[i],&w);
deg[beg[i]]++;
deg[en[i]]++;
sum+=w;
dis[beg[i]][en[i]]=min(dis[beg[i]][en[i]],w);
dis[en[i]][beg[i]]=min(dis[en[i]][beg[i]],w);
fa[ufind(beg[i])]=fa[ufind(en[i])];
}
for(int i=1;i<=m;i++)
{
if(ufind(beg[i])!=ufind(1)&&(en[i])!=ufind(1))
{
printf("-1");
return 0;
}
}
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
for(int i=1; i<=n; i++)
{
if(deg[i]&1) od[++tot]=i;
}
dfs(0,0,1);
printf("%d",sum+ans);
return 0;
}