poj2728 Desert King(0/1分数规划)(最小生成树)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83045130

题意

每条边都有两个值ci、ri,选择一棵生成树,使这棵树上的\frac{\sum_{i\in T} ci}{\sum_{i\in T} ri}最小。

题解

0/1分数规划+最小生成树判断

老套路,把公式转变成

\sum_{i=1}^{n} (c[i]-mid*r[i])*x[i]

因为其符合生成树的定义,所以对其二分需要以生成树的形式。即以c[i]-mid*r[i]为边权,做一次最小生成树,如果存在大于等于0的答案,说明mid大了,还可以更小,r=mid;否则l=mid。

总结

和 洛谷2868 [USACO07DEC]观光奶牛Sightseeing Cows 一起总结吧。

0/1分数规划一定是通过二分来确定最优值的,如何判断才是一个重难点。一般来说,它的原型是什么就用什么来判断,在图论中,把边权、点权改成 xx - yy * mid 的形式来check就好了。

代码

注释掉的部分本来想写堆优化的prim,结果写炸了....

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef pair<double,int> pdi;
const double eps=1e-5;//debug int
const int maxn=1010;

int n;
int X[maxn],Y[maxn],H[maxn];

double e[maxn*maxn];

double d[maxn];bool vis[maxn];
priority_queue<pdi> q;
double calc(int i,int j,double mid)
{
	double r=sqrt((double)(X[i]-X[j])*(X[i]-X[j])+(double)(Y[i]-Y[j])*(Y[i]-Y[j]));
	double c=abs(H[i]-H[j]);
	return c-mid*r;
}

double a[maxn][maxn];
double prim(double mid)//最小生成树 
{
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++) a[i][j]=a[j][i]=calc(i,j,mid);
	double re=0;
	memset(vis,false,sizeof(vis));vis[1]=true;
	d[0]=1<<30;d[1]=0;for(int i=2;i<=n;i++) d[i]=a[1][i];
	/*d[1]=0;for(int i=2;i<=n;i++) d[i]=1<<30; 
	q.push(make_pair(0,1));
	while(!q.empty())*/ 
	for(int i=2;i<=n;i++)
	{
		/*int x=q.top().second;q.pop();
		if(vis[x]) continue;*/
		int x=0;
		for(int j=1;j<=n;j++)
			if(!vis[j] && d[j]<d[x]) x=j;//debug j->i
		vis[x]=true;
		re+=d[x];
		for(int y=1;y<=n;y++)
			if(!vis[y] && a[x][y]<d[y]) d[y]=a[x][y];
		/*for(int y=1;y<=n;y++)
		{
			if(vis[y]) continue;
			double cc=calc(x,y,mid);
			if(cc<d[y])
			{
				d[y]=cc;
				q.push(make_pair(cc,y));
			}
		}*/
	}
	return re;
}

bool check(double mid)//判断比率能否小于mid 
{
	return prim(mid)<=eps;//debug >=
}

int main()
{
	while(scanf("%d",&n),n!=0)
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&X[i],&Y[i],&H[i]);
		}
		
		double l=0,r=1e5,ans;
		while(l-r<=eps)//while(l<r)
		{
			double mid=(l+r)/2;
			if(check(mid))
			{
				ans=mid; 
				r=mid-eps;
			} 
			else l=mid+eps;
		}
		printf("%.3f\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83045130
今日推荐