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

题意

每条边都有两个值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
#include
#include
#include
#include
using namespace std;
typedef pair 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 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]i
		vis[x]=true;
		re+=d[x];
		for(int y=1;y<=n;y++)
			if(!vis[y] && a[x][y]=
}

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

 

你可能感兴趣的:(刷题之路,0/1分数规划,最小生成树,《算法竞赛进阶指南》刷书之旅)