1001 Battle Over Cities - Hard Version 題解
阿新 • • 發佈:2021-10-24
題目描述:
輸入輸出樣例:
解題思路:
很顯然,求一個城市被佔領後,計算恢復剩下城市的聯通所需要的開支情況,這是一個經典的最小生成樹問題。筆者在這裡採用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; }