0/1分数规划(poj2976)

1 篇文章 0 订阅

输入数列{a_{1},a_{2},.....a_{n}}和{b_{1},b_{2},....b_{n}},从两个数列中去掉k对,选择n-k对,求100*\frac{\sum_{i=1}^{n}a_{i}s_{i}}{\sum_{i=1}^{n}b_{i}s_{i}}的最大值,s_{i}取1或0分别表示选或者不选第i对数。0<k<n<1000。

我们假设 \frac{\sum_{i=1}^{n}a_{i}s_{i}}{\sum_{i=1}^{n}b_{i}s_{i}}=x

移项得f=\sum_{i=1}^{n}(a_{i}-xb_{i})s_{i}=0

y_{i}=a_{i}-xb_{i},把x,y看成变量,这就是一条条直线,题意就转换成求一个最大的x使得n-k条直线函数值为0

这n条直线的图形如下图6.4,可以看出每一条直线都是单调递减的,因此他们的和也是单调递减,因此n-k条直线的函数值也是随着x单调递减的,意味着我们总能找到一个x使得n-k条直线函数值为0。

为了节省时间,就可以用二分,二分的一个端点肯定就是0,另一个端点就是\sum_{i=1}^{n}a_{i}

#include<bits/stdc++.h>
using namespace std;
int n,k;
struct node
{
	int a;
	int b;
	double y;
}h[1005];
bool tmp(node a,node b)
{
	return a.y>b.y;
}
bool check(double m)
{
	for(int i=0;i<n;i++)
	{
		h[i].y=h[i].a*1.0-m*h[i].b; 
	}
	sort(h,h+n,tmp);
	double  ans=0.0;
	for(int i=0;i<k;i++)
	{
		ans+=h[i].y;
	}
	if(ans>=0)return 1;
	else return 0;
}
int main()
{
	
	while(cin>>n>>k&&n+k!=0)
	{
		k=n-k;
		double l=0,r=0;
		for(int i=0;i<n;i++)
		{
			cin>>h[i].a;
			r+=h[i].a;
		}
		for(int i=0;i<n;i++)cin>>h[i].b;
		for(int i=0;i<=50;i++)
		{
			double mid=(l+r)/2;
			if(check(mid))
			{
				l=mid;
			}
			else
			{
				r=mid;
			}
		}
		cout<<(int)(100*l+0.5)<<endl;
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值