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;
}

浙公网安备 33010602011771号