PAT (Top Level) Practice 1001 Battle Over Cities - Hard Version 题解

题目描述:

输入输出样例:

解题思路:

很显然,求一个城市被占领后,计算恢复剩下城市的联通所需要的开支情况,这是一个经典的最小生成树问题。笔者在这里采用Kruskal算法进行求解。

对于题目中的联通状况,我们可在边列表存图的基础上,再加上一个用来记录该路段是否联通的变量,即可实现题意要求。

枚举每一个城市被占领的情况,记录下花费,最后输出所求花费最大的几个城市即可。

注意点:

当任将一个点从图中删去,无需新增加任何边即可保证剩下n-1个点联通时,输出0。

当将某个点从图中删去,剩下的点无法组成连通图时,将该城市损失代价记为正无穷(即2^31-1)。

代码:

#include <cstdio>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define INT_MAX 2147483647
using namespace std;
int max(int x,int y){
    return (x>y)?x:y;
}
int f[505],cnt[505],n,m;
bool vis[505],num[505];
struct edge{
    int x,y,val,stat;
}e[255025];//邻接矩阵存图
void readi(int &x){
    char c;
    for(c=getc(stdin);c<'0'||c>'9';c=getc(stdin));
    for(x=0;c<='9'&&c>='0';c=getc(stdin))x=(x<<3)+(x<<1)+c-'0';
}//快速读取
int getf(int x){
    if(f[x]!=x){
        f[x]=getf(f[f[x]]);
        return f[x];
    }
    return x;
}
bool merge(int x,int y){
    int fx=getf(x);
    int fy=getf(y);
    if(fx!=fy){
        f[fy]=fx;
        return false;
    }
    return true;
}//并查集部分
void init(){
    for(int i=1;i<=n;i++)f[i]=i;
}//初始化并查集
bool cmp(edge x,edge y){
	return x.val<y.val;
}
int main(){
    readi(n);readi(m);
    for(int i=1;i<=m;i++){
        readi(e[i].x);readi(e[i].y);readi(e[i].val);readi(e[i].stat); 
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++){
    	init();//每次尝试前重置并查集
    	memset(vis,false,sizeof(vis));
        int add_count = 0;
    	for(int j=1;j<=m;j++){
    	    if(e[j].x!=i&&e[j].y!=i&&e[j].stat==1){
                merge(e[j].x,e[j].y);
                add_count++;
                }
	}
        
	for(int j=1;j<=m;j++){
            if(add_count==n-2)break;//n-1个点的最小生成树边数为n-2
	    if(e[j].x==i||e[j].y==i||e[j].stat==1)continue;
		if(!merge(e[j].x,e[j].y)){
		    cnt[i]+=e[j].val;
                    add_count++;
	        }
	}
            if(add_count<n-2)cnt[i]=INT_MAX;
        
    }
    int max_cnt=0;
    for(int i=1;i<=n;i++)max_cnt=max(max_cnt,cnt[i]);
    queue<int> q; 
    if(max_cnt==0){//当敌方拿下任意一个城市后,剩余城市仍然保持联通时
	printf("0");
    }else{
	for(int i=1;i<=n;i++)if(max_cnt==cnt[i])q.push(i);//用队列来消去行末空格
    }
    while(!q.empty()){
	int tmp=q.front();
	q.pop();
	printf("%d",tmp);
	if(!q.empty())printf(" ");//不是最后一个数字则输出空格
    }
    return 0;
}                    

  

posted @ 2021-10-24 18:38  LuckyRowlet  阅读(66)  评论(0)    收藏  举报