解题报告:【kuangbin带你飞】专题九 连通图

目录

    • A、POJ 1236 Network of Schools(有向图缩点)
    • B、UVA 315 Network(找割点)
    • C、UVA 796 Critical Links(桥)
    • D、POJ 3694 Network
    • E、POJ 3177 Redundant Paths
    • F、HDU 4612 Warm up
    • G、HDU 4635 Strongly connected(最多可加边数使得仍然非强连通)
    • H、HDU 4685 Prince and Princess
    • I、HDU 4738 Caocao's Bridges(桥、任何位运算一定都要加括号、因为有重边所以用前向星)

A、POJ 1236 Network of Schools(有向图缩点)

A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B
.
You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school.

Input
The first line contains an integer N: the number of schools in the network (2 <= N <= 100). The schools are identified by the first N positive integers. Each of the next N lines describes a list of receivers. The line i+1 contains the identifiers of the receivers of school i. Each list ends with a 0. An empty list contains a 0 alone in the line.

Output
Your program should write two lines to the standard output. The first line should contain one positive integer: the solution of subtask A. The second line should contain the solution of subtask B.

tarjian缩点,因为一个强连通分块里面可以互相到达,那么可以当成一个点处理。
任务A:
需要求最多在多少学校发送新软件,其实就是求缩点后入度为0的个数(如果入度不为0就可以从其他学校传过来)
任务B:
求入度为0的点数与出度为0的点的较大值。

#include
#include
#include
#include
#include
#include

using namespace std;
typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int dfn[N], low[N];
int head[N], nex[M], ver[M], tot;
int scc_id[N], cnt_scc;
bool ins[N];
int stk[N], top;
vector<int>scc[N];

void add(int x, int y)
{
     
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}
int ind;

void tarjan(int x)
{
     
    dfn[x] = low[x] = ++ ind;
    stk[++ top] = x ;
    ins[x] = 1;
    for(int i = head[x]; ~i; i = nex[i]){
     
        int y = ver[i];
        if(!dfn[y]){
     
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if(ins[y])
            low[x] = min(low[x], dfn[y]);
    }
    if(dfn[x] == low[x]){
     
        int y;
        cnt_scc ++ ;
        do{
     
            y = stk[top -- ];
            ins[y] = 0;
            scc_id[y] = cnt_scc;
            scc[cnt_scc].push_back(y);
        }while(x != y);
    }
}

int ansi, anso;
int in[N], out[N];

int main()
{
     
    scanf("%d", &n);
    memset(head, -1, sizeof head);
    for(int i = 1; i <= n;++ i){
     
        int x;
        scanf("%d", &x);
        while(x){
     
            add(i, x);
            scanf("%d", &x);
        }
    }
    for(int i = 1; i <= n; ++ i){
     
        if(!dfn[i])
            tarjan(i);
    }

    for(int i = 1; i <= n; ++ i)
    {
     
        for(int j = head[i]; ~j; j = nex[j]){
     
            int k = ver[j];
            if(scc_id[i] != scc_id[k]){
     
                out[scc_id[i]] ++ ;
                in[scc_id[k]] ++ ;
            }
        }
    }

    for(int i = 1; i <= cnt_scc; ++ i){
     
        if(!out[i])
            anso ++ ;
        if(!in[i])
            ansi ++;
    }

    printf("%d\n", ansi);

    if(cnt_scc == 1){
     
        puts("0");
    }
    else {
     
        printf("%d\n", max(ansi, anso));
    }
    return 0;
}

B、UVA 315 Network(找割点)

A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting
several places numbered by integers from 1 to N. No two places have the same number. The lines
are bidirectional and always connect together two places and in each place the lines end in a telephone
exchange. There is one telephone exchange in each place. From each place it is possible to reach
through lines every other place, however it need not be a direct connection, it can go through several
exchanges. From time to time the power supply fails at a place and then the exchange does not operate.
The officials from TLC realized that in such a case it can happen that besides the fact that the place
with the failure is unreachable, this can also cause that some other places cannot connect to each other.
In such a case we will say the place (where the failure occured) is critical. Now the officials are trying
to write a program for finding the number of all such critical places. Help them.

Input
The input file consists of several blocks of lines. Each block describes one network. In the first line
of each block there is the number of places N < 100. Each of the next at most N lines contains the
number of a place followed by the numbers of some places to which there is a direct line from this place.
These at most N lines completely describe the network, i.e., each direct connection of two places in
the network is contained at least in one row. All numbers in one line are separated by one space. Each
block ends with a line containing just ‘0’. The last block has only one line with N = 0.

Output
The output contains for each block except the last in the input file one line containing the number of
critical places

多组数据读入一个数n,n=0时退出,代表n个点的无向图。下面若干行,每行第一个数为u,u=0时退出,后面若干个数v,代表和u相连的点v,遇到换行退出。对于每组数据,输出图的割顶个数

输入比较麻烦,然后就是找割点的模板了

#include
#include
#include
#include
#include
using namespace std;

const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
int n, m;
int dfn[N], low[N], ind;
int head[N], ver[M], nex[M], tot;
int stk[N], top;
int cut[N], num;
bool bridge[N];
int cnt;
bool ins[N];
vector<int>dcc[N];

void add(int x, int y)
{
     
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}
int root;

void tarjan(int x)
{
     
    dfn[x] = low[x] = ++ num;
    stk[++ top] = x;
    int flag = 0;
    for(int i = head[x]; ~i; i = nex[i]){
     
        int y = ver[i];
        if(!dfn[y]){
     
            tarjan(y);
            low[x] = min(low[x], low[y]);
            if(low[y] >= dfn[x]){
     
                flag ++ ;
                if(x != root || flag > 1)cut[x] =  true;
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}

int main()
{
     
    while(~scanf("%d", &n) && n){
     
        cnt = tot = num = root = top = 0;
        for(int i = 1; i <= n; ++ i)
        dcc[i].clear();
        memset(head, -1 ,sizeof head);
        memset(cut, 0, sizeof cut);
        memset(dfn, 0, sizeof dfn);
        memset(low, 0, sizeof low);
        int x, y;
        while(~scanf("%d", &x) && x){
     
            while(1){
     
                char ch = getchar();
                if(ch == '\n')break;

                scanf("%d", &y);
                add(x, y);
                add(y ,x);
            }
        }
        for(int i = 1; i  <= n; ++ i){
     
            if(!dfn[i])
                root = i, tarjan(i);
        }

        int ans = 0;

        for(int i = 1; i <= n; ++ i)
            if(cut[i])ans ++ ;

        printf("%d\n", ans);
    }
    return 0;
}

C、UVA 796 Critical Links(桥)


求桥的模板
就是输入输出有点恶心,可以用格式化scanf输入。

#include
#include
#include
#include
#include

using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second

const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
int n, m;
int dfn[N], low[N], ind;
int head[N], ver[M], nex[M], tot;
int stk[N], top;
int cut[N], num;
bool bridge[N];
int cnt;
bool ins[N];
int bridge_num;
vector<int>dcc[N];

void put1(){
      puts("YES");}
void put2(){
      puts("NO") ;}
void put3(){
      puts("-1") ;}

void add(int x, int y)
{
     
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}

void init()
{
     
    num = tot = bridge_num = cnt = 0;
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(low, 0, sizeof low);
    memset(bridge, 0, sizeof bridge);
}

void tarjan(int x, int fa)
{
     
    dfn[x] = low[x] = ++ num;
    for(int i = head[x] ;~i; i = nex[i]){
     
        int y = ver[i];
        if(!dfn[y]){
     
            tarjan(y, x);
            low[x] = min(low[x], low[y]);
            if(dfn[x] < low[y]){
     
                bridge[i] = bridge[i ^ 1] = true, bridge_num ++ ;
            }
        }
        else if(fa != y) 
            low[x] = min(low[x], dfn[y]);
    }
}

int main()
{
     
    while(~scanf("%d", &n)){
     
        init();
        for(int i = 1; i <= n; ++ i){
     
            int x, y;
            scanf("%d (%d)", &x, &m);
            for(int j = 1; j <= m; ++ j)
            scanf("%d", &y), add(x, y), add(y, x);
        }
        for(int i = 1; i <= n; ++ i)
            if(!dfn[i])
                tarjan(i, -1);
                
        vector<PII>ans;

        for(int i = 0; i < tot;i += 2){
     
            if(bridge[i]){
     
                if(ver[i] < ver[i ^ 1])ans.push_back({
     ver[i], ver[i ^ 1]});
                else ans.push_back({
     ver[i ^ 1], ver[i]});
            }
        }

        sort(ans.begin(), ans.end());
        
        cout << bridge_num << " critical links" << endl;
        for(int i = 0; i < (int)ans.size(); ++ i)
            printf("%d - %d\n", ans[i].x, ans[i].y);
        puts("");
    }
    return 0;
}

D、POJ 3694 Network

E、POJ 3177 Redundant Paths

F、HDU 4612 Warm up

G、HDU 4635 Strongly connected(最多可加边数使得仍然非强连通)

HDU 4635 Strongly connected

Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected.
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point.

Input
The first line of date is an integer T, which is the number of the text cases.
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.

Output
For each case, you should output the maximum number of the edges you can add.
If the original graph is strongly connected, just output -1.

给定一个有向图,求最大可以增加多少条边使得这个图仍然不是强连通图。

解题报告:【kuangbin带你飞】专题九 连通图_第1张图片

图片来源

  • 最大可以增加多少条边使得这个图仍然不是强连通图

结论: n ∗ ( n − 1 ) − m − m i n ∗ ( n − m i n v ) n * (n - 1) - m - min * (n - minv) n(n1)mmin(nminv)(其中n为点数、m为边数、minv为缩点以后的点中入度和出度至少有一个为0的包含节点个数最少的点)

#include
#include
#include
#include
#include
using namespace std;

const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int head[N], ver[M], nex[M], tot;
int dfn[N], low[N], ind;
int stk[N], top;
int scc_id[N];
int scc_cnt;
bool ins[N];
int scc_num[N];
vector<int>scc[N];
int t;

void add(int x, int y){
     
    ver[tot] = y;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}

void tarjan(int x)
{
     
    dfn[x] = low[x] = ++ ind;
    stk[++ top] = x, ins[x] = true;
    for(int i = head[x]; ~i ; i = nex[i]){
     
        int y = ver[i];
        if(!dfn[y]){
     
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if(ins[y])
            low[x] = min(low[x], dfn[y]);
    }
    if(dfn[x] == low[x]){
     
        int y;
        scc_cnt ++;
        do{
     
            y = stk[top -- ];
            ins[y] = false;
            scc_id[y] = scc_cnt;
            scc_num[scc_cnt] ++ ;
            scc[scc_cnt].push_back(y);
        }while(x != y);
    }
}

int in[N], out[N];
int ans;
int cnt;
int main()
{
     
    scanf("%d", &t);
    while(t -- ){
     
        cnt ++ ;
        tot = 0, ind = 0, scc_cnt = 0;
        memset(dfn, 0, sizeof dfn);
        memset(low, 0, sizeof low);
        memset(head, -1, sizeof head);
        memset(in, 0, sizeof in);
        memset(out, 0, sizeof out);
        memset(scc_id, 0,sizeof scc_id);
        memset(scc_num, 0, sizeof scc_num);

        scanf("%d%d", &n, &m);
        for(int i = 1; i <= m; ++ i){
     
            int x, y;
            scanf("%d%d", &x, &y);
            add(x, y);
        }

        for(int i = 1; i <= n; ++ i)
            if(!dfn[i])
                tarjan(i);

        for(int i = 1; i <= n; ++ i){
     
            for(int j = head[i]; ~j; j = nex[j]){
     
                int k = ver[j];
                if(scc_id[i] != scc_id[k]){
     
                    out[scc_id[i]] ++ ;
                    in[scc_id[k]] ++ ;
                }
            }
        }

        int minv = INF;
        for(int i = 1; i <= scc_cnt; ++ i){
     
            if(!in[i] || !out[i]){
     
                minv = min(minv, scc_num[i]);//找到包含点最少并且出度和入度至少有一个为0的点
            }
        }

        if(scc_cnt == 1){
     
            printf("Case %d: -1\n", cnt);
        }
        else {
     
            printf("Case %d: %lld\n", cnt, 1ll * n * (n - 1) - m - minv * (1ll * n - minv));
        }
    }
    return 0;
}

H、HDU 4685 Prince and Princess

I、HDU 4738 Caocao’s Bridges(桥、任何位运算一定都要加括号、因为有重边所以用前向星)

Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn’t give up. Caocao’s army still was not good at water battles, so he came up with another idea. He built many islands in the Changjiang river, and based on those islands, Caocao’s army could easily attack Zhou Yu’s troop. Caocao also built bridges connecting islands. If all islands were connected by bridges, Caocao’s army could be deployed very conveniently among those islands. Zhou Yu couldn’t stand with that, so he wanted to destroy some Caocao’s bridges so one or more islands would be seperated from other islands. But Zhou Yu had only one bomb which was left by Zhuge Liang, so he could only destroy one bridge. Zhou Yu must send someone carrying the bomb to destroy the bridge. There might be guards on bridges. The soldier number of the bombing team couldn’t be less than the guard number of a bridge, or the mission would fail. Please figure out as least how many soldiers Zhou Yu have to sent to complete the island seperating mission.

Input
There are no more than 12 test cases.
In each test case:
The first line contains two integers, N and M, meaning that there are N islands and M bridges. All the islands are numbered from 1 to N. ( 2 <= N <= 1000, 0 < M <= N2 )
Next M lines describes M bridges. Each line contains three integers U,V and W, meaning that there is a bridge connecting island U and island V, and there are W guards on that bridge. ( U ≠ V and 0 <= W <= 10,000 )
The input ends with N = 0 and M = 0.

Output
For each test case, print the minimum soldier number Zhou Yu had to send to complete the mission. If Zhou Yu couldn’t succeed any way, print -1 instead.

题意:曹操有一个无向图,图中有一些边,周瑜现在要派一些人去炸一条边,使曹操的无向图分为不同部分,曹操的每条边上都有守卫,周瑜派的人不能少于边上守卫数,问最少要派多少人。

  • 注意重边处理, 要用链式前向星。
  • 守卫为0时,也需要派一个人;
  • 如果原本图不连通(tarjan多次),就不需要派人,答案就是0。
  • 答案直接输出所有桥中的最小边权。
#include
#include
#include
#include
using namespace std;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
int bridge[N];
int dfn[N], low[N], num;
int head[N], ver[M], nex[M], edge[M], tot;
int ans;
int dcc_cnt;
int min_bridge;

void init()
{
     
    num = tot = dcc_cnt = 0;
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(low, 0, sizeof low);
    min_bridge = INF;
}

void add(int x, int y, int z)
{
     
    ver[tot] = y;
    edge[tot] = z;
    nex[tot] = head[x];
    head[x] = tot ++ ;
}

void tarjan(int x, int in_edge)
{
     
    dfn[x] = low[x] = ++ num;
    for(int i = head[x] ;~i; i = nex[i]){
     
        int y = ver[i], z = edge[i];
        if(!dfn[y]){
     
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if(dfn[x] < low[y]){
     
                bridge[i] = bridge[i ^ 1] = true;
                min_bridge = min(min_bridge, z);
            }
        }
        else if(i != (in_edge ^ 1))
            low[x] = min(low[x], dfn[y]);
    }
}

int main()
{
     
    while(~scanf("%d%d", &n, &m) && n ){
     
        init();

        for(int i = 1; i <= m; ++ i){
     
            int x, y ,z;
            scanf("%d%d%d", &x, &y, &z);
            add(x, y, z), add(y, x, z);
        }
        dcc_cnt = 0;
        for(int i = 1; i <= n; ++ i)
            if(!dfn[i])
                tarjan(i, 0), dcc_cnt ++ ;
        
        if(dcc_cnt > 1){
     //tarjan多次说明不连通,直接输出0
            printf("0\n");
            continue;
        }
        if(min_bridge == INF)min_bridge = -1;
        else if(min_bridge == 0)min_bridge = 1;
        printf("%d\n", min_bridge);
    }
    return 0;
}

你可能感兴趣的:(#,有向图的强连通分量,#,无向图的连通性,kuangbin专题合集)