Desert King POJ2728(Prim+迭代+0-1分数规划)
Posted Harry_bh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Desert King POJ2728(Prim+迭代+0-1分数规划)相关的知识,希望对你有一定的参考价值。
【原题地址】: 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分数规划)的主要内容,如果未能解决你的问题,请参考以下文章
poj2728 Desert King最优比率生成树Prim0/1分数规划