图论强连通分量算法,个人感觉tarjan相比两次dfs好写一点(个人看法)
这三道题都在学了强连通分量算法之后都比较基础,貌似都要判断一下缩点之后每个点的入度?
P1262 间谍网络
题意:
直接复制一下数据的输入格式这里,还是比较好理解的吧
第一行只有一个整数n。
第二行是整数p。表示愿意被收买的人数,1≤p≤n。
接下来的p行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过20000。
紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),A间谍掌握B间谍的证据。
那么对于数对(A,B),我们可以使A指向B建图,之后tarjan算法求强连通分量缩点
tarjan算法退栈的时候,需要判断一下每一个点内部是否都有可以收买的间谍,并且求出内部的点最小的编号
最后处理一下所有的点的入度,检查入度为0的点,如果有一个点的内部没有可以收买的间谍的话直接输出它最小的编号就可以了
代码:
//Decision's template
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<string>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define DP_maxn 16
#define maxn 100000
#define INF 10000007
#define mod 1000000007
#define mst(s,k) memset(s,k,sizeof(s))
typedef long long ll;
struct Edge{
int from,to,dist;
Edge(int u,int v,int d):from(u),to(v),dist(d){}
};
/*-------------------------------template End--------------------------------*/
int n,need[maxn],p,r,ans = 0,minid[maxn];
int First[maxn],Next[maxn],Last[maxn],a[maxn],k = 0;
int dfn[maxn],low[maxn],top = 0,q[maxn],fa[maxn],sum = 0,minn[maxn],cnt = 0,s[maxn],in[maxn];
bool instack[maxn],flag[maxn];
void init()
{
mst(First,0);
mst(low,0);
mst(dfn,0);
mst(s,0);
mst(in,0);
for(int i = 0;i<=3000+10;i++) need[i] = INF,minid[i] = INF,minn[i] = 0;
}
void add(int x,int y)
{
k++;
a[k] = y;
if(First[x]==0) First[x] = k;
else Next[Last[x]] = k;
Last[x] = k;
}
void tarjan(int x)
{
cnt++;top++;
low[x] = dfn[x] = cnt;
q[top] = x;
instack[x] = true;
int t = First[x];
while(t!=0)
{
int v = a[t];
if(dfn[v]==0)
{
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(instack[v]) low[x] = min(low[x],dfn[v]);
t = Next[t];
}
if(dfn[x]==low[x])
{
sum++;
while(q[top+1] != x)
{
int tt = q[top];
minid[sum] = min(minid[sum],tt);
//cout<<need[tt]<<" "<<need[minn[sum]]<<" "<<need[tt]<<endl;
if(need[tt]!=0&&need[minn[sum]]>need[tt]) {minn[sum] = tt;flag[sum] = 1;}
//cout<<minn[sum]<<endl;
s[sum]++;
fa[tt] = sum;
instack[tt] = false;
top--;
}
}
//cout<<minn[sum]<<endl;
}
int main()
{
//freopen("std.in","r",stdin);
//freopen("std.out","w",stdout);
init();
cin>>n>>p;
for(int i = 1;i<=p;i++)
{
int id,money;
cin>>id>>money;
need[id] = money;
}
cin>>r;
for(int i = 1;i<=r;i++)
{
int tmpx,tmpy;
cin>>tmpx>>tmpy;
add(tmpx,tmpy);
}
for(int i = 1;i<=n;i++)
{
if(dfn[i]==0) tarjan(i);
}
for(int i = 1;i<=n;i++)
{
for(int j = First[i];j;j = Next[j])
{
int y = a[j];
if(fa[i]!=fa[y]) in[fa[y]]++;
}
}
for(int i = 1;i<=sum;i++)
{
if(in[i]==0)
{
if(flag[i]==0) {cout<<"NO"<<endl<<minid[i]<<endl;return 0;}
else ans+=need[minn[i]];
}
}
cout<<"YES"<<endl<<ans<<endl;
return 0;
}
P2341 [HAOI]受欢迎的牛
N个点M条有向边,给出一点的关系构建一个有向图
求强连通分量,因为在一个强连通分量内部,一个点可以到达其他的所有点,那么也就是说在里面一头牛会认为其他的牛都是受欢迎的,那么可以缩点
缩点之后,遍历一下所有的点,求出缩点之后各点的入度,出度为0的点为所求答案,ans++
代码:
//Decision's template
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<string>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define DP_maxn 16
#define maxn 100000
#define INF 10000007
#define mod 1000000007
#define mst(s,k) memset(s,k,sizeof(s))
typedef long long ll;
struct Edge{
int from,to,dist;
Edge(int u,int v,int d):from(u),to(v),dist(d){}
};
/*-------------------------------template End--------------------------------*/
int n,m,tmpx,tmpy,top = 0,k = 0,cnt = 0;
int First[maxn],Next[maxn],Last[maxn],a[maxn*2],pe[maxn],ppe[maxn];
int dfn[maxn],tmp = 0,low[maxn],q[maxn],fa[maxn];
bool instack[maxn];
void init(){
mst(First,0);
mst(Next,0);
mst(a,0);
mst(dfn,0);
mst(low,0);
}
void add(int x,int y)
{
k++,a[k] = y;
if(First[x]==0) First[x] = k;
else Next[Last[x]] = k;
Last[x] = k;
}
void tarjan(int x)
{
top++;cnt++;
dfn[x] = low[x] = cnt;
q[top] = x;
instack[x] = true;
int t = First[x];
while(t!=0){
int v = a[t];
if(dfn[v] == 0){
tarjan(v);
if(low[v]<low[x]) low[x] = low[v];
}
else if(instack[v]&&dfn[v] < low[x]) low[x] = dfn[v];
t= Next[t];
}
if(dfn[x] == low[x])
{
n++;
while(q[top+1]!=x)
{
pe[n]++;
fa[q[top]] = n;
instack[q[top]] = false;
top--;
}
}
}
int main()
{
//freopen("std.in","r",stdin);
//freopen("std.out","w",stdout);
init();
cin>>n>>m;
for(int i = 1;i<=m;i++)
{
cin>>tmpx>>tmpy;
add(tmpx,tmpy);
}
m = n+1;
for(int i = 1;i<=m-1;i++)
if(dfn[i]==0) tarjan(i);
for(int i = 1;i<=m-1;i++){
for(int o = First[i];o;o = Next[o])
if(fa[i]!=fa[a[o]]) ppe[fa[i]]++;
}
int pp = 0;
for(int i = m;i<=n;i++) if(ppe[i] ==0) pp++;
if(pp==1) cout<<pe[m]<<endl;
else cout<<"0"<<endl;
return 0;
}
P2002 消息扩散
和上面一题差不多,就是在入度为0的点发布就可以让全部城市都得到消息
//Decision's template
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#include<string>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define DP_maxn 16
#define maxn 100000*6
#define INF 10000007
#define mod 1000000007
#define mst(s,k) memset(s,k,sizeof(s))
typedef long long ll;
struct Edge{
int from,to,dist;
Edge(int u,int v,int d):from(u),to(v),dist(d){}
};
/*-------------------------------template End--------------------------------*/
int n,m,tmpx,tmpy,ans = 0;
int First[maxn],Next[maxn],Last[maxn],k = 0,ru[maxn];
int q[maxn],top = 0,a[maxn],dfn[maxn],low[maxn],cnt = 0,fa[maxn],num[maxn],sum = 0;
bool instack[maxn];
void add(int x,int y)
{
k++;
a[k] = y;
if(First[x]==0) First[x] = k;
else Next[Last[x]] = k;
Last[x] = k;
}
void tarjan(int x)
{
top++;cnt++;
dfn[x] = low[x] = cnt;
q[top] = x;
instack[x] = true;
int t = First[x];
while(t!=0)
{
int v = a[t];
if(dfn[v]==0){
tarjan(v);
if(low[v]<low[x]) low[x] = low[v];
}
else if(instack[v]&&dfn[v]<low[x]) low[x] = dfn[v];
t = Next[t];
}
if(dfn[x]==low[x])
{
sum++;
while(q[top+1]!=x)
{
num[sum]++;
fa[q[top]] = sum;
instack[q[top]] = false;
top--;
}
}
}
void init()
{
mst(dfn,0);
mst(low,0);
mst(num,0);
mst(fa,0);
}
int main()
{
//freopen("std.in","r",stdin);
//freopen("std.out","w",stdout);
init();
cin>>n>>m;
for(int i = 1;i<=m;i++){
cin>>tmpx>>tmpy;
add(tmpx,tmpy);
}
for(int i = 1;i<=n;i++)
{
if(dfn[i]==0) tarjan(i);
}
for(int i = 1;i<=n;i++)
{
for(int j = First[i];j;j = Next[j])
{
int v = a[j];
if(fa[i]!=fa[v]) ru[fa[v]]++;
}
}
for(int i = 1;i<=sum;i++)
{
if(ru[i]==0) ans++;
}
cout<<ans<<endl;
return 0;
}