刷題總結——保留道路(ssoj)
題目:
題目背景
161114-練習-DAY1-AHSDFZ T3
題目描述
很久很久以前有一個國家,這個國家有 N 個城市,城市由 1,2,3,…,,N 標號,城市間有 M 條雙向道路,每條道路都有兩個屬性 g 和 s ,兩個城市間可能有多條道路,並且可能存在將某一城市與其自身連接起來的道路。後來由於戰爭的原因,國王不得不下令減小花費從而關閉一些道路,但是必須要保證任意兩個城市相互可達。
道路花費的計算公式為 wG*max{所有剩下道路的屬性g}+wS*max{所有剩下道路的屬性s},其中 wG 和 wS 是給定的值。國王想要在滿足連通性的前提下使這個花費最小,現在需要你計算出這個花費。
輸入格式
第一行包含兩個正整數 N 和 M 。
第二行包含兩個正整數 wG 和 wS 。
後面的 M 行每行描述一條道路,包含四個正整數 u,v,g,s,分別表示道路連接的兩個城市以及道路的兩個屬性。
輸出格式
輸出一個整數,表示最小花費。若無論如何不能滿足連通性,輸出 -1 。
樣例數據 1
輸入 [復制]
3 3
2 1
1 2 10 15
1 2 4 20
1 3 5 1
輸出
30
備註
【數據規模與約定】
對於 10% 的數據,N≤10;M≤20;
對於 30% 的數據,N≤100;M≤1000;
對於 50% 的數據,N≤200;M≤5000;
對於 100% 的數據,N≤400;M≤50000;wG,wS,g,s≤1000000000
題解:
30 分做法: 按照 g 屬性從小到大排序,枚舉 maxG,對滿足 maxG 的所有道路 按照 s 屬性從小到大排序,然後做 kruskal,時間復雜度O(M^2logM)。
50 分做法: 在 30 分基礎上,發現每次只增加一條邊,插入到上次的邊集合中再做 kruskal 即可,時間復雜度 O(M^2)。
100 分做法: 依舊按照 g 屬性從小到大排序。丌斷加入新邊的過程中發現,當前的最小生成樹只可能是由未加入新邊的最小生成樹的邊和當前新邊組成的共 N 條邊中選出 N-1 條構成。因此維護一個最小生成樹邊集,每次只在 N 條邊中做最小生成樹,時間復雜度 O(MN)。
代碼:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=50001; struct node { int go; int to; int g; int s; }ed[N]; int n,m,fa[401]; int sta[401],tot,num; long long wG,wS; long long ans=2e18+5; inline int Ri() { char c; int i=1,f=0; for(c=getchar();(c>‘9‘||c<‘0‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) { i=-1; c=getchar(); } for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f+(f<<2)<<1)+(c^‘0‘); return f*i; } inline long long Rl() { char c; long long i=1,f=0; for(c=getchar();(c>‘9‘||c<‘0‘)&&c!=‘-‘;c=getchar()); if(c==‘-‘) { i=-1; c=getchar(); } for(;c<=‘9‘&&c>=‘0‘;c=getchar()) f=(f+(f<<2)<<1)+(c^‘0‘); return f*i; } inline int getfa(int x) { if(fa[x]==x) return x; else return getfa(fa[x]); } inline void comb(int x,int y) { int Fx=getfa(x); int Fy=getfa(y); if(Fx!=Fy) fa[Fx]=Fy; } bool comp(node a,node b) { if(a.g==b.g) return a.s<b.s; return a.g<b.g; } int main() { //freopen("a.in","r",stdin); n=Ri(); m=Ri(); wG=Rl(); wS=Rl(); for(int i=1;i<=m;i++) { ed[i].go=Ri(); ed[i].to=Ri(); ed[i].g=Ri(); ed[i].s=Ri(); } sort(ed+1,ed+m+1,comp); tot=0; int j; for(int i=1;i<=m;i++) { for(j=1;j<=n;j++) fa[j]=j; for(j=tot;j>=1;j--) { if(ed[sta[j]].s>ed[i].s) sta[j+1]=sta[j]; else break; } tot++,sta[j+1]=i; num=0; for(j=1;j<=tot;j++) { int Fg=getfa(ed[sta[j]].go); int Ft=getfa(ed[sta[j]].to); if(Fg!=Ft) { fa[Fg]=Ft; sta[++num]=sta[j]; } } if(num==n-1) ans=min(ans,ed[sta[num]].s*wS+ed[i].g*wG); tot=num; } if(ans==2e18+5) cout<<"-1"<<endl; else cout<<ans<<endl; return 0; }
刷題總結——保留道路(ssoj)