1. 程式人生 > >洛谷 P1262|P2341|P2002 強連通分量,縮點

洛谷 P1262|P2341|P2002 強連通分量,縮點

圖論強連通分量演算法,個人感覺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;
}