Desert King POJ2728(Prim+叠代+0-1分數規劃)
阿新 • • 發佈:2018-04-11
生成 display c代碼 ace 超時 amp %d lin .org 【原題地址】:
若最小生成樹邊權總和 \(≥ 0\),則說明 \(k\) 值小,否則 \(k\) 值偏大。
如果用二分可能會超時
我們設 \(MST(now)\) 為 \(k=now\) 時最小生成樹的結果
那麽可以發現當 \(now\) 越大則 \(MST(now)\) 值越小
所以我們采用叠代,用上次計算的值,計算當前值
【原題地址】: POJ2728
【題目大意】:
選 \(n-1\) 條邊,最小化這些邊的\[\frac{\sum w_i} {\sum d_i}\]
其中 \(w_i\) 為第 \(i\) 條邊的花費,\(d_i\) 為這條邊所連接的兩個點的距離。
\(w_i\) 為連接的兩個點的 \(z\) 值差的絕對值, \(d_i\) 為歐幾裏得距離
【題解】:
這道題是0-1分數規劃的經典題目
我們令\[\frac{\sum w_i} {\sum d_i}=k\]
這樣二分 \(k\) 的值
將所有的邊權改為 \(w_i - k*d_i\)
用 Prim 求出最小生成樹
註: 這道題負邊很多,用堆優化prim會超時
若最小生成樹邊權總和 \(≥ 0\),則說明 \(k\) 值小,否則 \(k\) 值偏大。
如果用二分可能會超時
我們設 \(MST(now)\) 為 \(k=now\) 時最小生成樹的結果
那麽可以發現當 \(now\) 越大則 \(MST(now)\) 值越小
所以我們采用叠代,用上次計算的值,計算當前值
【AC代碼】:
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; const int maxl=1e3+10; int n; int abs(int x) { return x>0?x:-x; } double dis[maxl][maxl]; int x[maxl],y[maxl],z[maxl],cost[maxl][maxl]; int vis[maxl]; double to[maxl]; double tot_cost,tot_dis; int reach[maxl]; void prim(double mid) { vis[1]=1; for(int i=2;i<=n;i++)to[i]=cost[1][i]-dis[1][i]*mid,vis[i]=0,reach[i]=1; tot_cost=tot_dis=0; for(int i=1;i<=n-1;i++) { int pos; double minn=1e50; for(int j=2;j<=n;j++) if(!vis[j]) { if(minn>to[j]) { minn=to[j]; pos=j; } } vis[pos]=1; tot_cost+=cost[reach[pos]][pos]; tot_dis+=dis[reach[pos]][pos]; for(int j=2;j<=n;j++) if(!vis[j]){ if(to[j]>cost[pos][j]-dis[pos][j]*mid)to[j]=cost[pos][j]-dis[pos][j]*mid,reach[j]=pos; } } } int main() { while(~scanf("%d",&n)&&n) { for(int i=1;i<=n;i++) scanf("%d%d%d",&x[i],&y[i],&z[i]); for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) dis[i][j]=dis[j][i]=sqrt((x[i]*1.0-x[j])*(x[i]*1.0-x[j])+(y[i]*1.0-y[j])*(y[i]*1.0-y[j])),cost[i][j]=cost[j][i]=abs(z[i]-z[j]); double ans=0,last=1e100; while(abs(ans-last)>1e-4) { prim(ans); last=ans; ans=tot_cost/tot_dis; } printf("%.3f\n",ans); } }
Desert King POJ2728(Prim+叠代+0-1分數規劃)