【题目链接】
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=106786#problem/D
【解题报告】
给定n个点和m条边的无向连通图,和k个特殊点,任意两个特殊点之间连一条路,任意一个特殊点不能是两条路的端点。问最多能连多少条路。
这个题目我是不会的(图论思维真的很弱。。。)。
这个题还是重点要学习怎么分析题目的。
对于一个连通块,我们通过深搜把它变成树,如果它内部有2i个匹配点,则两两匹配。如果有2i+1个匹配点,那么就出现一个不能匹配的点,它需要上溯和他的父节点或者兄弟子树中的节点来匹配。依据此来进行DFS搜索。
一个连通块里如果有k个特殊点,那么一定存在k/2条路。注意题目没有说明给出的图是联通图,所以数据可能是多个联通块,处理的时候还需要维护一下路径总数。
寻找路径的方法是DFS。因为没有写过这类题,不太清楚这种把图当作树处理的代码的实现细节。所以学习了一下别人的代码。
http://blog.csdn.net/playwfun/article/details/49618017
这里面对容器使用了泛型迭代器.for+auto指遍历这个容器的所有元素。需要学习使用这种迭代器(比iterator简洁)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+100;
int pa[maxn],dep[maxn],link[maxn];
bool mark[maxn],vis[maxn];
vector<int>G[maxn];
int n,m,k;
int ans=0;
int dfs( int u, int fa )
{
vis[u]=1; pa[u]=fa;
dep[u]=(fa==-1 ? 0 : dep[fa] )+1;
int now=mark[u]?u:-1;
for( auto v:G[u] )
{
if(vis[v])continue;
int It=dfs( v,u ); //返回一个子树内一个没有匹配的特殊点
if(It==-1)continue;
if(now==-1)now=It; //以特殊点为起点
else{
link[now]=It;
link[It]=now;
now=-1;
ans++;
}
}
return now;
}
void print()
{
memset( vis,0,sizeof vis );
for( int i=1;i<=n;i++ )if( link[i] && !vis[i] )
{
vis[i]=vis[link[i]]=1;
int u=i,v=link[i];
vector<int>lt,rt;
lt.push_back(u);
rt.push_back(v);
int cnt_=0;
while( u!=v ){
if(dep[u]<dep[v])rt.push_back(pa[v]),v=pa[v];
else lt.push_back(pa[u]),u=pa[u];
}
for( int i=rt.size()-2;i>=0;i-- )lt.push_back( rt[i] );
cout<<lt.size()-1;
for( int i=0;i<lt.size();i++ )
cout<<" "<<lt[i];
cout<<endl;
}
}
int main()
{
cin>>n>>m>>k;
int x,y;
for( int i=1;i<=m;i++ )
{
int x,y; cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
for( int i=1;i<=k;i++ )
{
int x; cin>>x;
mark[x]=true;
}
for( int i=1;i<=n;i++ )if(!vis[i])dfs(i,-1);
cout<<ans<<endl;
print();
return 0;
}