您的位置:首页 > 其它

图论1 Tarjan算法

2017-04-14 09:46 99 查看
强连通分量

模板(强联通分量个数+缩点)





#include<iostream>
#include<cstdio>
#define MAXn 100000
#define MAXm 2000000
using namespace std;
int dfn[MAXn],low[MAXn],head[MAXm],st[MAXn],belong[MAXn];
bool in_st[MAXn];
int ans,n,m,num,s_num,cnt,group_num;
struct node{
int to,pre;
}e[MAXm];
void Insert(int from,int to){
e[++num].pre=head[from];
e[num].to=to;
head[from]=num;
}
void group(int u){
cnt++;st[++s_num]=u;dfn[u]=low[u]=cnt;in_st[u]=1;
for(int i=head[u];i;i=e[i].pre){
int v=e[i].to;
if(!dfn[v]){
group(v);
if(low[v]<low[u])low[u]=low[v];
}
else if(dfn[v]<low[u])
if(in_st[v])
low[u]=dfn[v];
}
if(dfn[u]==low[u]){
group_num++;
while(st[s_num]!=u){
in_st[st[s_num]]=0;
belong[st[s_num]]=group_num;
s_num--;
}
in_st[u]=0;s_num--;
belong[u]=group_num;
}
}
int main(){
freopen("Tarjan_group.txt","r",stdin);
scanf("%d%d",&n,&m);int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
Insert(x,y);
}
for(int i=1;i<=n;i++){
if(!dfn[i])group(i);
}
for(int i=1;i<=n;i++){
printf("%d ",belong[i]);
}printf("\n%d",group_num);
}


Tarjan_group模板

例题

poj3114  Countries in War

跑一边强连通分量,搞出缩点,求缩点间的最短路





#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxm 250010
#define maxn 510
int head[maxm],dfn[maxn],low[maxn],st[maxn],belong[maxn],blong[maxn][maxn];
int n,m,num,order,cnt,s_num,num_g,dis[maxn][maxn];
int nw[510][510];
bool in_st[maxn];
struct node{
int to,v,pre;
}e[maxm];
void Insert(int from,int to,int v){
e[++num].to=to;
e[num].pre=head[from];
e[num].v=v;
head[from]=num;
}
void group(int u){
cnt++;dfn[u]=low[u]=cnt;st[++s_num]=u;in_st[u]=1;
for(int i=head[u];i;i=e[i].pre){
int v=e[i].to;
if(!dfn[v]){
group(v);
if(low[v]<low[u])low[u]=low[v];
}
else if(dfn[v]<low[u])
if(in_st[v])low[u]=dfn[v];
}
if(low[u]==dfn[u]){
num_g++;
while(st[s_num]!=u){
blong[num_g][++blong[num_g][0]]=st[s_num];
belong[st[s_num]]=num_g;
in_st[st[s_num]]=0;
s_num--;
}belong[u]=num_g;
blong[num_g][++blong[num_g][0]]=u;
in_st[u]=0;s_num--;
}
}
int main(){
freopen("Tarjan_group.txt","r",stdin);
int nm,nn;
while(1){
cin>>n>>m;
if(n==0&&m==0)break;
memset(dfn,0,sizeof(dfn));
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(st,0,sizeof(st));
memset(e,0,sizeof(e));
memset(belong,0,sizeof(belong));
memset(blong,0,sizeof(blong));
num_g=0;s_num=0;cnt=0;
int x,y,z;
memset(nw,127/3,sizeof(nw));
memset(dis,127/3,sizeof(dis));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
Insert(x,y,z);
nw[x][y]=z;
}
for(int i=1;i<=n;i++){
if(!dfn[i])group(i);
}

/*for(int i=1;i<=n;i++)
cout<<belong[i]<<' ';cout<<endl<<endl;*/
for(int i=1;i<=n;i++){
dis[i][i]=0;
nw[i][i]=0;
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j&&i!=k&&k!=j)
nw[i][j]=min(nw[i][j],nw[i][k]+nw[k][j]);
}
}
}
for(int i=1;i<=num_g;i++){
for(int j=1;j<=num_g;j++){//找到两个缩点
if(i!=j){
for(int k=1;k<=blong[i][0];k++){
for(int l=1;l<=blong[j][0];l++){
if(k!=l)
dis[i][j]=min(dis[i][j],nw[blong[i][k]][blong[j][l]]);
}
}
}
}
}
int p;
scanf("%d",&p);
for(int i=1;i<=p;i++){
scanf("%d%d",&x,&y);
if(dis[belong[x]][belong[y]]>=707406378)cout<<"Nao e possivel entregar a carta"<<endl;
else cout<<dis[belong[x]][belong[y]]<<endl;
}
}
}


TLE





#include<cstring>
#include<cstdio>
#include<iostream>
#define Max 505
using namespace std;
int map[Max][Max];
struct Edge{
int to,w;
int next;
}edge[Max * Max];
int head[Max],tol;
void add(int u,int v,int w){
edge[tol].to=v;
edge[tol].w=w;
edge[tol].next=head[u];
head[u]=tol++;
}
int dfn[Max],low[Max],Stack[Max],belong[Max];
int bcnt,time,top,instack[Max];
void tarjan(int u){
dfn[u]=low[u]=++time;
Stack[top++]=u;
instack[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else{
if( instack[v])
low[u] = min(low[u], dfn[v]);
}
}
int v;
if(low[u] == dfn[u]){
bcnt++;
do{
v = Stack[--top];
instack[v] = false;
belong[v] = bcnt;
}while(u != v);
}
}
int main(){
int n,m;
int v,u,w;
while(~scanf("%d%d", &n,&m)){
if(!n && !m) break;
memset(head,-1,sizeof(head));
tol=0;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
top=time=bcnt=0;
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
if(i==j)
map[i][j]=0;
else map[i][j]=0x3f3f3f3f;
}

for(u=1;u<=n;u++)  {
for(int j=head[u];j!=-1;j=edge[j].next)  {
v=edge[j].to;
w=edge[j].w;
if(belong[u]!=belong[v])
map[belong[u]][belong[v]]=min(map[belong[u]][belong[v]],w);
}
}
for(int k=1;k<=bcnt;k++){
for(int i=1;i<=bcnt;i++){
for(int j=1;j<=bcnt;j++)
if(map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
}
}
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&u,&v);
if(map[belong[u]][belong[v]] == 0x3f3f3f3f)
printf("Nao e possivel entregar a carta\n");
else
printf("%d\n",map[belong[u]][belong[v]]);
}
printf("\n");
}
return 0;
}


AC
 

poj1236Network of Schools

题意:一个包含1-n号学校的网络,每个学校有个软件分发列表,当学校拿到软件时会把软件分发给列表里的学校。 
问1:一个新软件出现时初始化情况至少需要给多少个学校才能让它到达整个网络? 
问2:至少需要添加多少个名单才能使从任意一个学校开始分发都能充满整个网络? 

也就是: 
—给定一个有向图,求:

1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点

2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

DAG上面有多少个入度为0的顶点,问题1的答案就是多少 
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少 

加边的方法:假定有 n 个入度为0的点,m个出度为0的点,max(m,n)就是第二个问题的解





#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 110
#define maxm (maxn*(maxn - 1)/2)
int n,num,cnt,num_g,num_in0,num_out0;
int in[maxn],out[maxn],head[maxm],belong[maxn],dfn[maxn],low[maxn],st[maxn],s_num;
int blong[maxn][maxn],nmp[maxn][maxn];
bool s_in[maxn],vis[maxn][maxn];
struct node{
int to,pre;
}e[maxm];
void Insert(int from,int to){
e[++num].pre=head[from];
e[num].to=to;
head[from]=num;
}
void group(int u){
cnt++;dfn[u]=low[u]=cnt;st[++s_num]=u;s_in[u]=1;
for(int i=head[u];i;i=e[i].pre){
int v=e[i].to;
if(dfn[v]==0){
group(v);
low[u]=min(low[u],low[v]);
}
else if(dfn[v]){
/*if(s_in[v])*/low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]){
num_g++;
while(st[s_num]!=u){
s_in[st[s_num]]=0;
belong[st[s_num]]=num_g;
blong[num_g][++blong[num_g][0]]=st[s_num];
s_num--;
}
s_num--;belong[u]=num_g;
blong[num_g][++blong[num_g][0]]=u;
}
}
int main(){
freopen("1236.txt","r",stdin);
scanf("%d",&n);int a;
for(int i=1;i<=n;i++){
while(1){
scanf("%d",&a);
if(a==0)break;
Insert(i,a);
}
}
for(int i=1;i<=n;i++){
if(dfn[i]==0)group(i);
}

/*for(int i=1;i<=num_g;i++){//枚举每个缩点
for(int j=1;j<=blong[i][0];j++){//枚举缩点里的每个点
int u=i;
for(int k=head[blong[i][j]];k;k=e[k].pre){
int v=belong[e[k].to];
if(vis[u][v]==0&&v!=u){
vis[u][v]=1;in[v]++;out[u]++;
}
}
}
}*/
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].pre){
int v=e[i].to;
if(belong[u]!=belong[v]){
in[belong[v]]++;
out[belong[u]]++;
}
}
}
for(int i=1;i<=num_g;i++){
if(in[i]==0)num_in0++;
if(out[i]==0)num_out0++;
}
cout<<num_in0<<endl;
if(num_g==1)cout<<0;
else cout<<max(num_in0,num_out0);
return 0;
}


wa
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: