您的位置:首页 > 其它

poj1236|poj3177 tarjan,强联通,有向/无向

2016-04-04 14:56 274 查看
强连通是指内部任意点出发都能到达其余各点。

至于这题为什么要用连通块缩点,看完下面这张图就明白了。。



#include<iostream>
#include<algorithm>
#include<string>
#include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};
#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}
#include<vector>
#include<cmath>
#include<stack>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define lowbit(x) (x) & (-x)
#define mod 1000000007
#define ll long long
using namespace std;
#define maxn 105
int n;
vector<int> x[maxn];
int in[maxn],out[maxn];
//tarjan【强连通缩点】模板,【有向】
int dfn_clock,cnt;
int low[maxn],dfn[maxn],instack[maxn],suo[maxn];
stack<int> st;
void init_tarjan(){
for(int i=0;i<n;++i){
instack[i]=1;
}
while(!st.empty()) st.pop();
memset(dfn,0,sizeof(dfn));
dfn_clock=1;
cnt=0;  //缩完后图上有几个点
}
void tarjan(int p){
dfn[p]=low[p]=dfn_clock++;
st.push(p);
for(int i=0;i<x[p].size();++i){
int w=x[p][i];
if(!dfn[w]){
tarjan(w);
low[p]=min(low[p],low[w]);
}
else if(instack[w]){
low[p]=min(low[p],dfn[w]);
}
}
if(low[p]==dfn[p]){
while(!st.empty()){
int w=st.top();
suo[w]=cnt; //这个强联通里的点都是这个下标
st.pop();
instack[w]=0;
if(w==p)
break;
}
cnt++;
}
}
int main(){
cin>>n;
init_tarjan(); //别忘
int a;
for(int i=0;i<n;++i){
while(cin>>a&&a!=0){
a--;
x[i].push_back(a); //从i指向a有一条边
}
}
//开始用【tarjan】缩点了!!
for(int i=0;i<n;++i){
if(!dfn[i])
tarjan(i);
}//结束
for(int i=0;i<n;++i){
for(int j=0;j<x[i].size();++j){
int w=x[i][j];
if(suo[i]==suo[w]) continue; //同一个强联通里的,i,w已经看作一个点,再分就没意义了
out[suo[i]]++;
in[suo[w]]++;
}
}
int s1=0,s2=0;
//下面只要计算【缩点后】入度为0的点和出度为0的点各有几个
for(int i=0;i<cnt;++i){
if(in[i]==0)
s1++;
if(out[i]==0)
s2++;
}
if(cnt==1) //整个图缩点后只剩一个点,特判
cout<<1<<endl<<0<<endl;
else
cout<<s1<<endl<<max(s1,s2)<<endl; //缩点后图上入度为零的点都是起点,入度和出度可以用线互消
return 0;
}

【poj3177】给一个无向图,图中每个点都能到达其余各点。现问最少加几条边,使得图中任意两点可以用完全不相同的两条路径到达(一段都不能重合)。



#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
//tarjan【双连通缩点】模板,【无向】
const int maxn=5005;//点数
const int maxm=10005*2;//边数,因为是无向图,所以这个值要*2
struct Edge{
int to,next;
bool cut;//是否是桥标记
}edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],st[maxn],suo[maxn];//suo数组的值是1~cnt
int dfn_clock,top;
int cnt;//边双连通块数
bool instack[maxn];
int du[maxn];//缩点后形成树,每个点的度数
int bridge;//桥的数目

void addedge(int u,int v){
edge[tot].to=v;edge[tot].next=head[u];edge[tot].cut=false;
head[u]=tot++;
}
void init(){
memset(du,0,sizeof(du));
tot=0;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(instack,false,sizeof(instack));
dfn_clock=top=cnt=0;
}
void tarjan(int u,int pre){
int v;
low[u]=dfn[u]=++dfn_clock;
st[top++]=u;
instack[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].to;
if(v==pre)continue;
if( !dfn[v] ){
tarjan(v,u);
if( low[u]>low[v] )low[u]=low[v];
if(low[v]>dfn[u]){
bridge++;
edge[i].cut=true;
edge[i^1].cut=true;
}
}
else if( instack[v] && low[u]>dfn[v] )
low[u]=dfn[v];
}
if(low[u]==dfn[u]){
do{
v=st[--top];
instack[v]=false;
suo[v]=cnt;
}
while( v!=u );
cnt++;
}
}
int main(){
int n,m;
int u,v;
while(scanf("%d%d",&n,&m)!=EOF) {
init();
while(m--){
scanf("%d%d",&u,&v);
u--;v--;
addedge(u,v);
addedge(v,u);
}
//开始用【tarjan】缩点了!!
for(int i=0;i<n;++i){
if(!dfn[i])
tarjan(i,-1);
}//结束
for(int i=0;i<n;i++)
for(int j=head[i];j!=-1;j=edge[j].next)
if(edge[j].cut)
du[suo[i]]++; //计算每个点的度数
int ans=0;
for(int i=0;i<cnt;i++)
if(du[i]==1)
ans++;
printf("%d\n",(ans+1)/2);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: