参考资料:http://www.byvoid.com/blog/scc-tarjan/
几道简单的练习题(hdoj):
强连通:
1269 迷宫城堡 判断是否是一个强连通
2767 Proving Equivalences 至少加几条边让整个图变成强连通
3836 Equivalent Sets 至少加几条边让整个图变成强连通
1827 Summer Holiday 传递的最小费用
3072 Intelligence System 传递的最小费用
3861 The King’s Problem 强连通+二分匹配
3639 Hawk-and-Chicken 强连通缩点 + 树形dp(累加子节点的总权值)
3594 Cactus 仙人掌图
我的代码:
HDOJ1269 迷宫城堡(风格基本参照byvoid资料)
(模板测试题)
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
const int NN=10005;
int n,m;
int Dindex,Stop,Bcnt;
bool instack[NN];
int dfn[NN],low[NN];
int Stap[NN];
vector<int> map[NN];
void tarjan(int u)
{
int v;
dfn[u]=low[u]=++Dindex;
instack[u]=true;
Stap[++Stop]=u;
for (int i=0; i<map[u].size(); i++)
{
v=map[u][i];
if (!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (instack[v])
low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u])
{
Bcnt++;
do
{
v=Stap[Stop--];
instack[v]=false;
}while (v!=u);
}
}
void solve()
{
int i;
Stop=Bcnt=Dindex=0;
memset(dfn,0,sizeof(dfn));
memset(instack,false,sizeof(instack));
tarjan(1);
for (i=2; i<=n; i++)
{
if (!dfn[i])
{
Bcnt=2;
return ;
}
}
}
int main()
{
int i,a,b;
while (scanf("%d%d",&n,&m)==2)
{
if (n==0 && m==0) break;
for (int i=1; i<=n; i++) map[i].clear();
for (int i=0; i<m; i++)
{
scanf("%d%d",&a,&b);
map[a].push_back(b);
}
solve();
if (Bcnt>1) printf("No\n");
else printf("Yes\n");
}
return 0;
}
HDOJ2767(HDOJ3836题目是一样的,改改输入方式就过了):
(先求连通块,若图已强连通,不用添边;若连通块之间完全不连通则要添加bcnt(连通块数量)条边;若有x1个连通块有入边,则添bcnt-x1条边即可;若有x2个连通块有出边,则添bcnt-x2条边即可。第一种情况特判,后三种情况即是无入边连通块数a与无出边连通块数b的最大值)
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
const int NN=20005;
const int MM=50010;
vector<int> map[NN];
int n,m,bcnt,top,index;
int dfn[NN],low[NN],stack[NN],belong[NN],chu[NN],ru[NN];
int from[MM],to[MM];
bool instack[NN];
void init()
{
bcnt=0;
top=0;
index=0;
memset(instack,false,sizeof(instack));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
}
void tarjan(int u)
{
dfn[u]=low[u]=++index;
instack[u]=true;
stack[top++]=u;
int i,j,v;
for (i=0; i<map[u].size(); i++)
{
v=map[u][i];
if (!dfn[v])
{
tarjan(v);
if (low[v]<low[u]) low[u]=low[v];
}
else if (instack[v] && dfn[v]<low[u]) low[u]=dfn[v];
}
if (low[u]==dfn[u])
{
bcnt++;
do
{
j=stack[--top];
instack[j]=false;
belong[j]=bcnt;
}while (u!=j);
}
}
void solve()
{
for (int i=1; i<=n; i++) if (!dfn[i]) tarjan(i);
if (bcnt==1)
{
printf("0\n");
return ;
}
for (int i=1; i<=bcnt; i++) ru[i]=chu[i]=0;
for (int i=1; i<=m; i++)
{
int x=belong[from[i]];
int y=belong[to[i]];
if (x!=y)
{
ru[y]++;
chu[x]++;
}
}
int a,b;
a=b=0;
for (int i=1; i<=bcnt; i++)
{
if (ru[i]==0) a++;
if (chu[i]==0) b++;
}
if (a>b) b=a;
printf("%d\n",b);
}
int main()
{
int cas;
scanf("%d",&cas);
while (cas--)
{
scanf("%d%d",&n,&m);
init();
for (int i=1; i<=n; i++) map[i].clear();
for (int i=1; i<=m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
map[a].push_back(b);
from[i]=a;
to[i]=b;
}
solve();
}
return 0;
}
HDOJ1827:
(求出所有无入边的连通块的最小权,这些最小权的和即答案)
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
using namespace std;
const int NN=1002;
const int MM=2004;
const int INF=0x7fffffff;
vector<int> map[NN];
int n,m,Dindex,top,Bcnt;
int dfn[NN],w[NN],low[NN],belong[NN],stack[NN],cost[NN];
bool instack[NN],ru[NN];
void tarjan(int u)
{
dfn[u]=low[u]=++Dindex;
stack[++top]=u;
instack[u]=true;
int v;
for (int i=0; i<map[u].size(); i++)
{
v=map[u][i];
if (!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (instack[v])
low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u])
{
Bcnt++;
do
{
v=stack[top--];
belong[v]=Bcnt;
instack[v]=false;
}while (v!=u);
}
}
void solve()
{
int u,v,i;
memset(dfn,0,sizeof(dfn));
memset(instack,false,sizeof(instack));
Dindex=top=Bcnt=0;
for (i=1; i<=n; i++) if (!dfn[i]) tarjan(i);
memset(ru,false,sizeof(ru));
for (u=1; u<=n; u++)
{
for (i=0; i<map[u].size(); i++)
{
v=map[u][i];
if (belong[u]!=belong[v]) ru[belong[v]]=true;
}
}
for (i=1; i<=Bcnt; i++) cost[i]=INF;
for (i=1; i<=n; i++)
{
if (!ru[belong[i]])
cost[belong[i]]=min(cost[belong[i]],w[i]);
}
int ans1=0;
int ans2=0;
for (i=1; i<=Bcnt; i++)
{
if (!ru[i]) { ans1++; ans2+=cost[i]; }
}
printf("%d %d\n",ans1,ans2);
}
int main()
{
while (scanf("%d%d",&n,&m)==2)
{
for (int i=1; i<=n; i++)
{
scanf("%d",&w[i]);
map[i].clear();
}
int a,b;
for (int i=1; i<=m; i++)
{
scanf("%d%d",&a,&b);
map[a].push_back(b);
}
solve();
}
return 0;
}
HDOJ3072(跟上题很类似,想起一些天做比赛时看到这题,居然傻了,数据范围又大~):
(求各强连通块最小入度和,注意有结点0所在连通块的最小入度为0)
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
const int NN=50005;
const int MM=100010;
const int INF=0x7fffffff;
struct Edge{
int u,v,w,next;
}edge[MM];
int n,m,Ecnt,Bcnt,Dindex,top;
int st[NN],dfn[NN],low[NN],belong[NN],stack[NN],cost[NN];
bool instack[NN];
void addedge(int u,int v,int w)
{
Ecnt++;
edge[Ecnt].u=u; edge[Ecnt].v=v; edge[Ecnt].w=w; edge[Ecnt].next=st[u];
st[u]=Ecnt;
}
void init()
{
Ecnt=0;
Dindex=top=Bcnt=0;
int i;
for (i=0; i<n; i++)
{
st[i]=dfn[i]=0;
instack[i]=false;
}
}
void tarjan(int u)
{
dfn[u]=low[u]=++Dindex;
stack[++top]=u;
instack[u]=true;
int i,v;
for (i=st[u]; i; i=edge[i].next)
{
v=edge[i].v;
if (!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (instack[v])
low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u])
{
Bcnt++;
do
{
v=stack[top--];
instack[v]=false;
belong[v]=Bcnt;
}while (v!=u);
}
}
void solve()
{
int i,k1,k2;
for (i=0; i<n; i++) if (!dfn[i]) tarjan(i);
for (i=1; i<=Bcnt; i++) cost[i]=INF;
cost[belong[0]]=0;
for (i=1; i<=Ecnt; i++)
{
k1=belong[edge[i].u]; k2=belong[edge[i].v];
if (k1!=k2) cost[k2]=min(cost[k2],edge[i].w);
}
int ans=0;
for (i=1; i<=Bcnt; i++) ans+=cost[i];
printf("%d\n",ans);
}
int main()
{
int i,a,b,c;
while (scanf("%d%d",&n,&m)==2)
{
init();
for (i=0; i<m; i++)
{
scanf("%d%d%d",&a,&b,&c);
addedge(a,b,c);
}
solve();
}
return 0;
}
图论啊,图论~
共勉。