poj 2976 Dropping tests(基础的01分数规划)

做了几天的01分数规划,总结一下吧。个人感觉无论是最优比例生成环还是最优比率生成树或是最小割神马的,基本思路都是一样的即参数搜索,设出一个函数f[λ],每次二分λ,直到f[λ] = 0。而最优比例生成环是基于最短路的01分数规划,最优比率生成树是基于最小生成树的01分数规划,最小割是基于最大流的01分数规划。


http://poj.org/problem?id=2976


大致题意:输入n和k,然后给出n个二元组,要求从中取出n-k个数使得它们 a数之和与b数之和最大。


转载:

题目求的是 max(∑a[i] * x[i] / (b[i] * x[i]))  其中a,b都是一一对应的。 x[i]取0,1  并且 ∑x[i] = n - k;

那么可以转化一下。  

令r = ∑a[i] * x[i] / (b[i] * x[i])  则必然∑a[i] * x[i] - ∑b[i] * x[i] * r= 0;(条件1)

并且任意的 ∑a[i] * x[i] - ∑b[i] * x[i] * max(r) <= 0  (条件2,只有当∑a[i] * x[i] / (b[i] * x[i]) = max(r) 条件2中等号才成立)


然后就可以枚举r , 对枚举的r, 求Q(r) = ∑a[i] * x[i] - ∑b[i] * x[i] * r  的最大值,为什么要求最大值呢?因为我们之前知道了条件2,所以当我们枚举到r为max(r)的值时,显然对于所有的情况Q(r)都会小于等于0,并且Q(r)的最大值一定是0.而我们求最大值的目的就是寻找Q(r)=0的可能性,这样就满足了条件1,最后就是枚举使得Q(r)恰好等于0时就找到了max(r)。而如果能Q(r)>0 说明该r值是偏小的,并且可能存在Q(r)=0,而Q(r)<0的话,很明显是r值偏大的,因为max(r)都是使Q(r)最大值为0,说明不可能存在Q(r)=0了,需要换方向搜索了、 

然后算法框架就出来了。


二分枚举r。对每个r。求出每个a[i] - b[i] * r; 然后排序,将最大的n-k个相加即为最Q(r)的最大值。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;

int n,k;
int a[maxn],b[maxn];
double c[maxn];

int main()
{
	while(~scanf("%d %d",&n,&k) && (n || k))
	{
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		for(int i = 1; i <= n; i++)
			scanf("%d",&b[i]);

		double l = 0.0;
		double r = 1.0;
		double mid;

		while(fabs(r-l) > eps)
		{
			mid = (l+r)/2;

			for(int i = 1; i <= n; i++)
				c[i] = 1.0*a[i] - mid * 1.0 * b[i];
			sort(c+1,c+1+n);
			double sum = 0.0;
			for(int i = k+1; i <= n; i++)
				sum += c[i];
			if(sum > 0)
				l = mid;
			else if(sum < 0)
				r = mid;
			else break;
		}
		mid = mid*100;
		printf("%.0f\n",mid);
	}
	return 0;
}

Dinkelbach 算法,迭代

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-7
using namespace std;
const _LL INF = 1e18;
const int maxn = 1010;

int n,k;
int a[maxn],b[maxn];
double c[maxn];

struct node
{
	double d;
	int c;
	bool operator < (const struct node &t)const
	{
		return d < t.d;
	}
}tmp[maxn];

int main()
{
	while(~scanf("%d %d",&n,&k))
	{
		if(n == 0 && k == 0) break;
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		for(int i = 1; i <= n; i++)
			scanf("%d",&b[i]);

		double l = 0.0,r;
		double p,q;
		while(1)
		{
			for(int i = 1; i <= n; i++)
				tmp[i] = (struct node){a[i] - l*b[i], i};

			sort(tmp+1,tmp+1+n);
			p = 0; q = 0;
			for(int i = k+1; i <= n; i++)
			{
				p += a[tmp[i].c];
				q += b[tmp[i].c];
			}
			r = p/q;
			if(fabs(r-l) < eps) break;
			else l = r;
		}
		printf("%.0f\n",r*100);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值