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<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#include<stack>
#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]<dfn[u] && v!=fa){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if (fa<=0 && child==1){
        iscut[u]=false;
        cut_cnt--;
    }
}
void dfs(int u){//连通块
    vis[u]=true;
    cc[cc_cnt]++;
    for (int i=h[u];~i;i=ne[i]){
        int v=e[i];
        if (v==del || vis[v]){
            continue;
        }
        dfs(v);
    }
}
int main(){
    init();
    int n,m;
    scanf("%d%d",&n,&m);
    _for(i,1,m){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    int ans=0;
    _for(i,1,n){
        cut_cnt=cc_cnt=times=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(iscut,0,sizeof(iscut));
        memset(cc,0,sizeof(cc));
        memset(vis,0,sizeof(vis));
        del=i;
        if (i+1>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;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于扫雷游戏的题解,我可以给你一些思路和代码示例。首先,你需要了解扫雷游戏的规则和要求。接下来,你可以使用C++语言来实现游戏逻辑和界面。 下面是一个简单的扫雷游戏的C++代码示例: ```cpp #include <iostream> #include <vector> #include <random> using namespace std; class MinesweeperGame { private: int rows; int cols; vector<vector<char>> board; vector<vector<bool>> revealed; vector<pair<int, int>> directions = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}; public: MinesweeperGame(int m, int n, int mineCount) { rows = m; cols = n; board.resize(rows, vector<char>(cols, ' ')); revealed.resize(rows, vector<bool>(cols, false)); placeMines(mineCount); calculateNumbers(); } void printBoard() { cout << " "; for (int j = 0; j < cols; j++) { cout << j << " "; } cout << endl; for (int i = 0; i < rows; i++) { cout << i << " |"; for (int j = 0; j < cols; j++) { cout << board[i][j] << "|"; } cout << endl; } } bool isGameOver() { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (board[i][j] == 'M' && revealed[i][j]) { return true; } } } return false; } void reveal(int row, int col) { if (row < 0 || row >= rows || col < 0 || col >= cols || revealed[row][col]) { return; } revealed[row][col] = true; if (board[row][col] == 'M') { return; } if (board[row][col] == '0') { for (auto dir : directions) { reveal(row + dir.first, col + dir.second); } } } private: void placeMines(int mineCount) { random_device rd; mt1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值