1. 程式人生 > >[NOIP模擬][最大生成森林]拆牆

[NOIP模擬][最大生成森林]拆牆

題目描述:
地主的傻兒子豆豆家很大很大,由很多個區域組成。其中有不少封閉的區域,豆豆覺得很不爽於是決定拆牆,把家打通使得他可以訪問到每一個區域(包括家外面無限大的區域)。我們用 N 個端點和 M 條邊來描述豆豆的家。第 i 個端點的座標為(xi,yi),第 i 條邊連線端點 Ai 和 Bi,拆除所需要花費的力氣為 Ci 。保證所有邊只在端點相交,也就是這是一個平面圖,也沒有重邊和自環。
現在豆豆想知道他最少一共需要花費多少力氣?(拆牆數最少的前提下)
輸入格式:
第一行一個整數 T 表示資料組數。
每組資料第一行兩個整數 N,M 。
接下來 N 行每行兩個整數 XiYi
接下來 M 行每行三個整數 A

iBiCi
輸出格式:
每組資料輸出兩個整數表示最少拆除的牆的數量和拆牆最少需要多少的力氣。注意所有牆可能不互相連通。
樣例輸入:

1
4 4
-1 -1
-1 1
1 1
1 -1
1 2 1
2 3 2
3 4 1
4 1 2

樣例輸出:

1 1

資料範圍:
對於 30% 的資料,N,M10T=10
對於 70% 的資料,N5000, M10000T=1
對於 100% 的資料,N100000M200000ΣN300000ΣM500000|xi|,|yi|1000000wi10000T=3
題目分析:
因為題目說了保證所有邊只在端點相交,是一個平面圖,也沒有重邊和自環。所以這道題座標是沒有用的

(用處就是誤導大佬去寫對偶圖)。我們想,最後是讓圖中沒有封閉區域(即沒有環),那麼就是一棵樹。因為要刪掉的邊的權值和儘量小,就是剩餘邊的權值和要最大,於是就是做一棵最大生成樹,最後用總和減去就得到答案。因為最開始不一定只有一塊,於是就是做一個最大生成森林,最後得到多棵樹。
PS:最大生成森林做法和最小生成樹是一樣的,只是排序是從大到小。
附程式碼:
Kruskal求解:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime> #include<cmath> #include<cctype> #include<iomanip> #include<algorithm> using namespace std; const int N=1e5+10; const int M=2e5+10; int n,m,x,y,fa[N],fta,ftb,t,cnt,ans; struct node{ int x; int y; int w; }wall[M]; bool comp(const node &a,const node &b) { return a.w>b.w; } int getfa(int x) { if(fa[x]==x) return x; return fa[x]=getfa(fa[x]); } int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } int main() { //freopen("wall.in","r",stdin); //freopen("wall.out","w",stdout); t=readint(); while(t--) { ans=0; n=readint();m=readint(); for(int i=1;i<=n;i++) x=readint(),y=readint(); for(int i=1;i<=m;i++) { wall[i].x=readint(); wall[i].y=readint(); wall[i].w=readint(); ans+=wall[i].w; } cnt=m; for(int i=1;i<=n;i++) fa[i]=i; sort(wall+1,wall+m+1,comp); for(int i=1;i<=m;i++) { fta=getfa(wall[i].x); ftb=getfa(wall[i].y); if(fta!=ftb) { fa[fta]=ftb; ans-=wall[i].w; cnt--; } } printf("%d %d\n",cnt,ans); } return 0; }