【PTA练习】7-9 红色警报 (25 分)DFS深度优先遍历法

本文详细解析了7-9红色警报问题的算法实现,采用深度优先搜索(DFS)判断失去城市后对国家连通性的影响,通过记录攻陷城市与更新连通分支数,判断是否发出红色警报。

7-9 红色警报 (25 分)

*试题链接:https://pintia.cn/problem-sets/976477598334980096/problems/976477665661947912#p-9

题目:

战争中保持各个城市间的连通性非常重要。本题要求你编写一个报警程序,当失去一个城市导致国家被分裂为多个无法连通的区域时,就发出红色警报。注意:若该国本来就不完全连通,是分裂的k个区域,而失去一个城市并不改变其他城市之间的连通性,则不要发出警报。
输入格式:
输入在第一行给出两个整数N(0 < N ≤ 500)和M(≤ 5000),分别为城市个数(于是默认城市从0到N-1编号)和连接两城市的通路条数。随后M行,每行给出一条通路所连接的两个城市的编号,其间以1个空格分隔。在城市信息之后给出被攻占的信息,即一个正整数K和随后的K个被攻占的城市的编号。
注意:输入保证给出的被攻占的城市编号都是合法的且无重复,但并不保证给出的通路没有重复。
输出格式:
对每个被攻占的城市,如果它会改变整个国家的连通性,则输出Red Alert: City k is lost!,其中k是该城市的编号;否则只输出City k is lost.即可。如果该国失去了最后一个城市,则增加一行输出Game Over.。
输入样例:

5 4
0 1
1 3
3 0
0 4
5
1 2 0 4 3

输出样例:

City 1 is lost.
City 2 is lost.
Red Alert: City 0 is lost!
City 4 is lost.
City 3 is lost.
Game Over.

大致思路: 这个题就是用DFS深度优先遍历的方法判断有几个连通分支,并且去掉这个点以后会不会对这个图的连通分支数造成数量影响这种思路做的,其中在DFS的基础上还增加了一个f数组用于记录哪些城市已经被攻陷了。一开始的时候我没有想到通过设置这个数组来记录来解决,我考虑的是利用一个father数组即并查集中find的那种方式来记录其前驱结点根节点,结果写完代码运行之后显然不成emmm,然后吧我就开始找问题,越想越乱并且感觉代码写的啊不伦不类的,我就去百度了一下这个题,因为我知道我是在那个DFS的基础上做,考虑了一点并查集的思想,然后我百度的结果中很多都是用并查集做的,对比感觉用DFS的方法少一些,找到一个DFS的发现自己想这个题的思路中渐渐的忽视了被攻陷城市对整个题已经连通分支数的影响,主要的问题在两处一个就是father数组的设立不对,应该设立一个数组记录被攻陷的城市,还有一处是由于之前自己慢慢的把被攻陷城市对整个题已经连通分支数的影响给忘了,从而我把大概第60行左右if(sum0<=sum)这个条件中的<=给写成了==,后来对比人家的代码改了这两个问题之后,其他就是简单的DFS思想了,就基本上没有问题了。
具体代码如下:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;

int a[505][505];    int n, m, k;
int flag[505];	//DFS中经典的标记数组用于标记当前结点已经被访问
int f[505];  //记录被攻陷的城市,标记数组
void DFS(int i){
    flag[i]=1;
    for(int j=0;j<n;j++){
        if(flag[j]==0 && a[i][j]==1 && f[j]==0){	
        //两结点联通,并且当前结点即将走向的下一结点未被访问同时当前城市未被攻陷
            DFS(j);
        }
    }
}

int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++){         //初始化
        for(int j=0;j<n;j++){
            a[i][j] = -1;
        }
    }
    memset(flag, 0, sizeof(flag));//初始化
    memset(f, 0, sizeof(f));      //初始化
    for(int i=0;i<m;i++){
        int x, y;
        cin>>x>>y;
        a[x][y]=1;
        a[y][x]=1;
    }
    cin>>k;
    int sum = 0;				//初始化记录起始阶段的连通分支数,数量sum
    for(int i=0;i<n;i++){
        if(flag[i]==0){
            //flag[i]=1;
            sum++;
            DFS(i);
        }
    }
    for(int i=0;i<k;i++){
        int x;
        cin>>x;
        f[x]=1;					//当前城市被攻陷f由0->1
        for(int j=0;j<n;j++){	//将与此结点联通的所有其他结点间的路清除
            if(a[x][j]==1){
                a[x][j]=-1;
                a[j][x]=-1;
            }
        }
        memset(flag, 0, sizeof(flag));	//重新初始化,保证可进行下面的DFS
        int sum0=0;
        for(int j=0;j<n;j++){
            if(flag[j]==0 && f[j]==0){	//新的结点开始一个新的连通分支,且此节点为未被攻陷的城市
                //flag[j]=1;
                sum0++;
                DFS(j);
            }
        }
        if(sum0<=sum){					
        //当此城市被攻陷后,连通分支数应小于或等于上一状态下的连通分支数,即删去了一个相对无用的结点。
            cout<<"City "<<x<<" is lost."<<endl;
        }else{
            cout<<"Red Alert: City "<<x<<" is lost!"<<endl;
        }
        sum = sum0;

    }if(k==n){
        cout<<"Game Over."<<endl;
    }
    return 0;
}

此题还有用并查集的一个解法,附上我写代码时怼bug参考的一个博客:https://blog.youkuaiyun.com/qq_38735931/article/details/81778813
继续坚持吧!加油!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值