1. 程式人生 > 其它 >1001 Battle Over Cities - Hard Version 題解

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