1. 程式人生 > 實用技巧 >HDU 6763 Total Eclipse

HDU 6763 Total Eclipse

Total Eclipse

題目描述:

給一張無向圖,每個點有一個權值,每次可以選擇一個連通圖的消去最小的那個值。連通的點權值減去最小值。

權值為0的點就不見了,花銷為最小值。問把整個圖的點權值消到0需要多少花銷。

for example:

3 2

3 2 3

1 2

2 3

1與2連結 2與3連線 權值為3 2 3 開始3個點連通 花銷為2 權值變為 1 0 1,去掉2點 1和3不在連通分別花銷1。

所以最後結果為4。 (2+1+1)

原題連結:http://acm.hdu.edu.cn/showproblem.php?pid=6763

(菜雞的我,那場爆零了。。。太菜了)

思考:

這道題我當時試了很多辦法,重大點出發合併到其他小點上,或者重小店出發。給其他大點減去一個權值。但是

都是不行的。因為單點出發就得輻射全圖。如何保證這個全圖就很困難。自閉3小時啥也沒做出來。

後面看了題解幡然醒悟!!!!我當時想到了每次重全圖減去割點(不行)。卻沒有逆向思維我們。每次加一個點判斷

是否讓原圖形成一個新的連通圖!!其思想都是判斷該店對全圖的影響,但是換種思考方式就立馬變得可做了。

怎麼做?

首先判斷兩個點是否連通最好的辦法就是並查集找父親。規定小點做父親就行了。

其次對於新增的點做一個vis標記。確保目前在我的圖中有這個點。

最後給權值排序重大點開始新增。小點能成為兩個大點的聯通點。

就這麼簡單,但是比賽時沒想到。

#include<bits/stdc++.h>
using
namespace std; typedef long long ll; const int N = 1e5+10; int h[N],nt[N*5],to[N*5],fa[N],num,A[N],id[N]; bool vis[N]; bool cmp(int a,int b){ return A[a]>A[b]; } void add(int u,int v){ nt[++num]=h[u]; h[u]=num; to[num]=v; } int fid(int x){ if(x == fa[x]) return x; else return
fa[x]=fid(fa[x]); } int main(){ int t,n,m; scanf("%d",&t); while(t--){ num=0; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ vis[i]=h[i]=0; id[i]=fa[i]=i; scanf("%d",A+i); } sort(id+1,id+n+1,cmp);//按權值排大小 int a,b; for(int i=0;i<m;i++){ scanf("%d %d",&a,&b); add(a,b); add(b,a); } int pos = 1;//初始一個點,一個連通圖 ll ps = A[id[1]],ans=0; vis[id[1]]=1;//vis置1,表示在圖內。 for(int i=2;i<=n;i++){ ans+=(ps-A[id[i]])*pos; vis[id[i]]=1; pos++;//增加一個點,目前沒跟新邊,會增加一個獨立點,也就是怎加一個圖 for(int j=h[id[i]];j;j=nt[j]){ int v=to[j]; if(!vis[v]) continue;//不在圖中的點不計算 int fx=fid(v); int fy=fid(id[i]); if(fx!=fy){//兩個父親不一樣說明沒新增該點時是不連通的 if(fx>fy) fa[fx]=fy; else fa[fy]=fx; pos--; } } ps=A[id[i]]; } printf("%lld\n",ans+ps*pos);//最後剩下的不要忘記加上 } }

這道題思考很重要,難度不大。但是思維沒到那個地方。就是出不來。