题目大意:
有一个有V(V<=1000)个节点的图,每两个点之间都有边连接,所有边长为T。先给出E条指定的边,找出一条最短路(起点终点随意),使这条路径经过所有给定的边。
分析:
首先想一想,要是所有的边都没有公共节点该多好。那么答案就是(E-1)*T了。
可惜并不是这样,那么,我们就可以把几个有公共端点的边看成一个子图。对于一个子图,它要有一条边连接进来,再有一条边连出去(入口出口除外)。
所以就想到一个方法,对于一个子图,我们要在原图中加几条边使它联通,再把他们穿起来。想到了什么?欧拉道路!对于一个子图,我们把它变成一个”一笔画”图,添加的边数加上E,再加上连接各个子图的路径就是答案!
所以算法出来了:我们先统计各个点的度数,再DFS各个子图,求出要加上的边数,再用上文的方法就是解(这里用并查集不大方便,反正题目给的范围小,DFS更快捷)。
#include <cstdio>
#include <vector>
#include <set>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn=1000+20;
int dgr[maxn];
bool a[maxn];
vector<int> pile[maxn];
int dfs(int k)
{
int ans=0;
if (dgr[k]) ans++; //奇点,增加答案
for (int i=0;i<pile[k].size();i++)
{
int next=pile[k][i];
if (!a[next]) {a[next]=1;ans+=dfs(next);}
}
return ans;
}
int main()
{
int x,y,v,e,t,ca=0;
while ((scanf("%d%d%d",&v,&e,&t)==3) && (v || e || t)) //注意:有可能有V不为0,而E为0的情况
{
if ((e==0) || (v==0)) {printf("Case %d: 0\n",++ca);continue;} // 特殊判断
for (int i=0;i<v;i++) pile[i].clear(); //邻接表存边
memset(dgr,0,sizeof(dgr)); //度数,因为只要奇偶性,没必要存储实际度数
set<int> node; //给定的边涉及的点
node.clear();
for (int i=0;i<e;i++)
{
scanf("%d%d",&x,&y);
dgr[x]=1-dgr[x];
dgr[y]=1-dgr[y]; //改变奇偶性
node.insert(x);
node.insert(y);
pile[x].push_back(y);
pile[y].push_back(x);
}
set<int>::iterator it;
memset(a,0,sizeof(a)); //遍历标记
int ans=e-1; //因为有n个子图时,要加n-1条路径,下面加了n个,所以-1
for (it=node.begin();it!=node.end();it++)
if (!a[*it])
{
ans++; //子图之间的路径
a[*it]=1;
int p=dfs(*it); //子图奇点数
if (p>0) ans+=(p-2)/2; //加一条边少两个奇点,一个子图可以留两个奇点作为出入口
}
printf("Case %d: %d\n",++ca,ans*t);
}
return 0;
}
想想子图的问题,是不是很像tarjan呢……