题目:poj 3177 & 3352
题意:大概意思就是给你一个无向图,让你添加最少的边,让所有点都双连通。
分析:双连通的定义就是任意两个点至少有两条路可达。
其实做法跟添加最少边强连通一样,先对图中已经双连通的缩点,然后重新编号。
这就是著名的Tanjan算法。
通过搜索的思想对所有存在环的边遍相同的号
如果要让所有的点双连通,那么对于缩点后的图中如果度数为 1 的话,这些边肯定要连接到其他的点才能双连通,而题目要求添加最少,所以连接到其他度数也为 1 的点是最优的,那么答案就是(odd+1)/ 2
注意:图中存在重边,所以要判重。
AC代码:
#include <iostream>
#include <algorithm>
#include <string>
#include <math.h>
#include <vector>
#include <cstring>
#include <cstdio>
#include <stack>
using namespace std;
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 1100;
const int inf = 0x3f3f3f3f;
int vis[N],dfn[N],low[N];
vector<int> v[N];
struct Node
{
int from,to;
};
int bcc_cnt;
stack<Node> s;
void add_Node(int x,int y)
{
v[x].push_back(y);
v[y].push_back(x);
}
void Tarjan(int u,int fa)
{
vis[u]=1;
dfn[u]=low[u]=++bcc_cnt;
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
if(vis[to]==1 && to!=fa) //假如与其联通的点访问过了
low[u]=min(low[u],dfn[to]);
if(!vis[to])
{
Tarjan(to,u);
low[u]=min(low[u],low[to]);
}
}
vis[u]=2;
}
int in[N];
int Yougth(int n)
{
Del(in,0);
for(int i=0; i<n; i++)
{
for(int j=0; j<v[i].size(); j++)
{
int to = v[i][j];
//printf("%d %d %d %d\n",i+1,to+1,low[i],low[to]);
if(low[i]!=low[to])
{
in[low[i]]++;
}
}
}
int ans = 0;
for(int i = 1;i<=bcc_cnt;i++)
if(in[i]==1)
ans++;
return (ans+1)/2;
}
void Clear(int x)
{
for(int i=0;i<x;i++)
v[i].clear();
}
int main()
{
//freopen("Input.txt","r",stdin);
int n,m;
string s1,s2,cas;
while(cin>>s1>>s2>>cas)
{
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
int ok = 0;
for(int j=0;j<v[x-1].size();j++)
if(v[x-1][j]==(y-1)){
ok = 1;
break;
}
if(ok)
continue;
add_Node(x-1,y-1);
}
cout<<"Output for "<<s1<<" "<<s2<<" "<<cas<<endl;
Del(vis,0),Del(dfn,0),Del(low,0);
bcc_cnt = 0;
Tarjan(0,1);
printf("%d\n\n",Yougth(n));
Clear(n);
}
return 0;
}