首先说一下,身为一个初学者,虽然一遍就AC了,但实际上做这两道模板题花了不少时间,学这个东西还是不能着急,要尽量理解原理才去写代码,不然就算背下来也没什么意思。
POJ题目传送门:http://poj.org/problem?id=3177
http://poj.org/problem?id=3352
其实这两道题的代码都是一样的,因为3177看别人说好像有重边,与题目不符,所以我还是判断了一下。
题目主要说的意思就是有一个图,求添加最少条边数使原图变为双连通分量,所以我们就可以通过把原图缩点转化为一个更明显的图,由于是无向图,对其中度数<=2的进行计数,然后两两配对就好了。
具体公示为(total+1)/2,下面把代码贴上来,还有七张楼主自己做的图,希望有助于大家AC。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int MAXN=5001,MAXE=20001;
stack <int> s;
int cnt,first[MAXN],low[MAXN],dfn[MAXN],newnode[MAXN],Timetable,node,dgr[MAXN],tot;
struct ARC{
int go,next,num,self;
ARC(){
next=-1;
}
}arc[MAXE];
void add(int a,int b)
{
arc[++cnt].next=first[a];
arc[cnt].go=b;
arc[cnt].num=cnt;
arc[cnt].self=a;
first[a]=cnt;
}
void tarjan(int u,int num)
{
s.push(u);
low[u]=dfn[u]=++Timetable;
for(int i=first[u];i!=-1;i=arc[i].next)
{
int v=arc[i].go;
if((arc[i].num-1)/2!=num)
{
if(!dfn[v])
{
tarjan(v,(arc[i].num-1)/2);
low[u]=min(low[u],low[v]);
if(dfn[v]==low[v])
{
int point;
node++;
do{
point=s.top();
s.pop();
newnode[point]=node;
}while(point!=v);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
}
int main()
{
int t,r;
scanf("%d %d",&t,&r);
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(newnode,0,sizeof(newnode));
memset(dgr,0,sizeof(dgr));
memset(first,-1,sizeof(first));
for(int i=1;i<=2*r;i++) arc[i].next=-1;
cnt=Timetable=node=tot=0;
for(int i=1;i<=r;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=t;i++) if(!dfn[i])
{
tarjan(i,-1);
node++;
while(!s.empty())
newnode[s.top()]=node,s.pop();
}
for(int i=1;i<=cnt;i++)
if(newnode[arc[i].self]!=newnode[arc[i].go])
dgr[newnode[arc[i].self]]++,dgr[newnode[arc[i].go]]++;
for(int i=1;i<=node&&node>1;i++) if(dgr[i]<=2) tot++;
printf("%d\n",(tot+1)/2);
}