CSU2104: Extra Judicial Operation-Tarjan边双联通分量缩点两种方法-难受的bug

(有任何问题欢迎留言或私聊

题目链接:CSU2104

题面:2017年南太平洋某区域赛题

  • The Suitably Protected Programming Contest (SPPC) is a multi-site contest in which contestants compete at specified sites, which are linked by the Notorious Broken Network (NBN) of two-way links between some pairs of individual sites. There is at most one link directly between any pair of sites and there is no link from a site to itself. Contestants submit their solutions to a judging server to get them tested. The event will use one or more judging servers. Each judging server is located at a contest site and may be accessed directly from that site or through a sequence of linked sites. At any time during the contest, each contestant must have access to at least one judging server. The links between sites are set up in such a way that if all links work properly, it would suffice to operate one judging server. At the other extreme, having each site operate its own judging server would guarantee access even if all of the links failed. The organisers wish to minimise the number of judging servers that they operate, while still maintaining the integrity of the contest. Examination of the NBN’s performance over time reveals some good news. The NBN is reliably unreliable! At any given time, exactly one link is broken (that is, communications cannot travel in either direction on that link). Given this fact, the organisers want you to work out the minimum number of judging servers that must operate so that no matter which link is broken, every contestant still has access to at least one judging server. As they know that they must operate at least one judging server, they ask you to tell them how many extra judging servers they must operate.

题意:

我把题意换一种说法解释:
 n个点看成n个矿洞,m条无向边链接两个矿洞。在某些矿洞设置安全出口,这些边中的某一条有毁坏的可能,为了保证矿洞里所有工人的安全,使得无论哪一条边毁坏所有的工人都能逃到安全出口。求设置的安全出口的最小数量cnt。答案输出cnt-1。

类似题目:

P3225 [HNOI2012]矿场搭建
P2746 [USACO5.3]校园网Network of Schools

思路:

1.显然只有桥毁坏才能改变图的连通性,所以只有断桥才有意义。
2.对于无向图而言,桥连接的是两个边双联通分量(不懂双联通分量的点这里)。将边双联通分量缩点后变成一个简单图。
(以下所说的点全部指的是缩点之后的点)
3.但是 是不是缩点后所有的点都要设置一个安全出口呢?
4.显然不是。记住,题目说的是只断一条边。
5.如果一个点的度大于1,它就没有设置出口的必要,原因如下:
6.因为它的度数大于1,而断一条边最多改变这个点和另外一个点的连通性。这时,它可以通过其他边走到出口。
7.所以度数大于1的点没有设置出口的必要。
8.综上:求出缩点后度数<=1的点的数量cnt,输出cnt-1即可。

注意:

 题目没有保证图一定联通,而一般网上有两种tarjan缩点的写法,我下面提供的第二种写法处理图不连通的情况会有问题。第一种写法可以处理图不连通的情况。
 然后,csuoj上的数据没有涉及图不连通的情况。比如单独一个点的情况。下面代码中有简单解释到。。。。
我提供一组数据:

4
0 0 0 0

(这题一直没过,后来才发现是把vis[v]写成了vis[i],难受啊,马飞

AC代码1:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<stack>
#include<cstring>
#include<cassert>
using namespace std;
const int N = 100005;
int head[N],vis[N];
int n,tot,inde;
int dfn[N],low[N];
int qltNum;
int qltMap[N];
int du[N];
int st[N*2],top;
struct lp{
    int u,to,pre;
    bool is;
}cw[N*2];
void addedge(int x,int y){
    cw[++tot].to=y;cw[tot].pre=head[x];head[x]=tot;cw[tot].is=0;cw[tot].u=x;
}
void dfs(int u,int Fa){
    dfn[u] = low[u] = ++inde;
    vis[u]=1;int v;
    st[++top]=u;
    for(int i=head[u];~i;i=cw[i].pre){
        v=cw[i].to;
        if(v==Fa)continue;
        if(!vis[v]){
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            //if(low[v] > dfn[u]) bridge
        }else if(vis[v]==1){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){//就算只有单独一个点,联通块的个数还是无误的计算到了
        qltNum++;
        do{
            v=st[top--];
            vis[v]=2;
            qltMap[v]=qltNum;
        }while(v!=u);
    }
}
void tarjan(){
    for(int i=1;i<=n;++i){
        if(!vis[i]){
            dfs(i,-1);
        }
    }
}
void init(){
    qltNum=inde=top=0;
    tot=-1;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(qltMap,0,sizeof(qltMap));
    memset(du,0,sizeof(du));
}
int main(){
    while(~scanf("%d",&n)){
        init();
        for(int i=1;i<=n;++i){
            int x,y;
            scanf("%d",&x);
            while(x--){
                scanf("%d",&y);
                y++;
                addedge(i,y);
                addedge(y,i);
            }
        }
        tarjan();
        for(int i=0;i<=tot;i+=2){
            int a=cw[i].u,b=cw[i].to;
            if(qltMap[a]!=qltMap[b]){
                du[qltMap[a]]++;
                du[qltMap[b]]++;
            }
        }
        //printf("%d\n",qltNum);
        int cc=0;
        for(int i=1;i<=qltNum;++i){
            //printf("*%d\n",du[i] );
            if(du[i]<=1)cc++;
        }
        printf("%d\n",max(cc-1,0));
    }
    return 0;
}
AC代码2:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<stack>
#include<cstring>
using namespace std;
const int N = 100005;
int head[N],vis[N];
int n,tot,inde;
int dfn[N],low[N];
int qltNum;
int qltMap[N];
int du[N];
int st[N*2],top;
struct lp{
    int u,to,pre;
    bool is;
}cw[N*2];
void addedge(int x,int y){
 cw[++tot].to=y;cw[tot].pre=head[x];head[x]=tot;cw[tot].is=0;cw[tot].u=x;
}
void dfs(int u,int Fa){
    dfn[u] = low[u] = ++inde;
    vis[u]=1;int v;
    st[++top]=u;
    for(int i=head[u];~i;i=cw[i].pre){
        if(cw[i].is)continue;
        v=cw[i].to;
        if(v==Fa)continue;
        if(!vis[v]){
            cw[i].is=cw[i^1].is=1;
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]){//对于只有单独一个点的情况,这个for循环根本不会跑,qltNum就不会自加
                qltNum++;
                do{
                    qltMap[st[top]]=qltNum;
                }while(st[top--]!=v);
            }
        }else if(vis[v]==1){
            low[u]=min(low[u],dfn[v]);
        }
    }
}
void tarjan(){
    for(int i=1;i<=n;++i){
        if(!vis[i]){
            dfs(i,-1);
        }
    }
}
void init(){
    qltNum=inde=top=0;
    tot=-1;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(qltMap,0,sizeof(qltMap));
    memset(du,0,sizeof(du));
}
int main(){
    while(~scanf("%d",&n)){
        init();
        for(int i=1;i<=n;++i){
            int x,y;
            scanf("%d",&x);
            while(x--){
                scanf("%d",&y);
                y++;
                addedge(i,y);
                addedge(y,i);
            }
        }
        tarjan();
        for(int i=0;i<=tot;i+=2){
            int a=cw[i].u,b=cw[i].to;
            if(qltMap[a]!=qltMap[b]){
                du[qltMap[a]]++;
                du[qltMap[b]]++;
            }
        }
        //printf("%d\n",qltNum);
        int cc=0;
        for(int i=0;i<=qltNum;++i){
            //printf("*%d\n",du[i] );
            if(du[i]<=1)cc++;
        }
        printf("%d\n",max(cc-1,0));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值