C++题解:网络大战

目录

题目 

题解:




题目 

  •  1000ms
  •  131072K

一场无硝烟的战争即将爆发,蒜头君和花椰妹接到上级任务——破坏敌方通信网络。破坏敌方通信网络并不是一件简单任务,蒜头君和花椰妹只能各自破坏敌方通信网络中的一个节点。破坏两个节点后,如果敌方至少有两个节点无法通信(除了已被破坏的两个节点),就认为蒜头君和花椰妹破坏成功。为了简化问题,请你计算蒜头君和花椰妹有多少种方法可以成功破坏敌方通信网络。

输入格式

第一行输入两个整数 n(3≤n≤1000) 和 m(0≤m≤10000) ,表示敌方有 m 条双向电缆连接 n 个节点(节点编号从 1 到 n)。

接来下 m 行,每行有两个整数 a ,b(1≤a,b≤n) 表示节点 a 和节点 b 通过一条双向电缆连接。

输出格式

输出一个整数,表示蒜头君和花椰妹有多少种方法可以成功破坏敌方通信网络。

样例输入1

4 4
1 2
2 3
3 4
4 1

样例输出1

2

样例输入2

7 9
1 2
1 3
2 3
3 4
3 5
4 5
5 6
5 7
6 7

样例输出2

11



题解:

知识点:tarjan算法、割点、连通块

分析:

我们可以枚举每一个点,并假设这个点被蒜头君删除

当这个点被删除,若剩下的图被分成了≥3个连通块,那么只要删去了这个点,剩下n−1个点无论删去哪个都是有效答案,对答案的贡献为n-1

若剩下的图被分成了两个连通块:

  • 若有两个块都只有一个点,那么取哪一个点都不能使得该图不连通,因为拿哪一个点都会使该图还有一个点,故对答案的贡献为0
  • 若有一个块只有一个点,另一个块有≥2个点,则删去第一个块的唯一一个点后,原来两个块又变成了一个块,所以不能够删那个点。易证,删除另一个块的任意一个点都满足要求。此时这种情况对答案的贡献是n-2
  • 否则, 两个块都有≥2个点,任意取一个点都可行,贡献n-1

若剩下的图只有一个连通块,那么就只能取其割点了。

代码:

#include
#include
#include
#include
#include
#include
#define _for(i,a,b) for (int i=(a);i<=(b);i++)
using namespace std;
const int N=1e3+5,M=2e4+5;
struct node{
    int u,v;
    node(){}
    node(int _u,int _v){
        u=_u;
        v=_v;
    }
};
int e[M],ne[M],h[N],idx;//链式前向星
int del;//当前被删除的点
int times=0;//时间戳
int dfn[N],low[N];//时间戳、tarjan
int cc[N];//当前状态下第i个连通块的数量
int cut_cnt=0,cc_cnt=0;
bool iscut[N],vis[N];
void init(){
    memset(h,-1,sizeof(h));
    idx=0;
}
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
void tarjan(int u,int fa){
    dfn[u]=low[u]=++times;
    int child=0;
    for (int i=h[u];~i;i=ne[i]){
        int v=e[i];
        if (v==del){
            continue;
        }
        if (dfn[v]==0){
            child++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if (dfn[u]<=low[v] && !iscut[u]){
                iscut[u]=true;
                cut_cnt++;
            }
        }else if (dfn[v]n){
            tarjan(1,-1);
        }else{
            tarjan(i+1,-1);
        }
        _for(j,1,n){
            if (!vis[j] && j!=del){
                cc_cnt++;
                dfs(j);
            }
        }
        if (cc_cnt>=3){
            ans+=n-1;
        }else if (cc_cnt==2){
            if (cc[1]==1 && cc[2]>1 || cc[1]>1 && cc[2]==1){
                ans+=n-2;
            }else if (cc[1]>1 && cc[2]>1){
                ans+=n-1;
            }
        }else{
            ans+=cut_cnt;
        }
    }
    printf("%d\n",ans>>1);
    return 0;
}

你可能感兴趣的:(C++难题,c++,算法,图论,tarjan)