![【POJ 3621】[01分数规划]Dropping tests](/default/index/img?u=aHR0cHM6Ly9wMi5waXFzZWxzLmNvbS9wcmV2aWV3LzE1Mi8xMDE3LzEyNC9kZXNpZ24tbHV4dXJ5LW1pbnV0ZS1tb2Rlcm4uanBn&w=245&h=&w=700)
题意
给出n,k和n对二元组(a,b),要求从中选出n-k个二元组使得100×ΣaiΣbi最大。
原题链接–POJ
题目分析
我们假设答案为ans,那么一定对于任意方案有ans≥100×ΣaiΣbi,
而ans=max(100×ΣaiΣbi)时ans为最优解,
ans>max(100×ΣaiΣbi)时ans一定不是解,
故解在ans≤max(100×ΣaiΣbi)中
变一下形就可以得到max(Σ(100×ai−ans⋅bi))≥0。
若令g(ans)=max(Σ(100×ai−ans⋅bi)),我们很容易就可以发现其为单调的:g(ans)随着ans的增大而减小。
于是我们不妨二分ans再预处理出di=ai−ans⋅bi,从大到小排序后贪心算出g(ans)判定一下就行了。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 1000
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;
template<class T>
void Read(T &x){
x=0;char c=getchar();bool flag=0;
while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
if(flag)x=-x;
}
const double EPS = 1e-3;
int Sign(const double x){
if(x>EPS)return 1;
else if(x<-EPS)return -1;
else return 0;
}
bool dcmp(const double &x,const double &y){
return Sign(x-y)>0;
}
double a[MAXN+10],b[MAXN+10];
double d[MAXN+10];
int n,k;
bool check(double l){
for(int i=1;i<=n;++i)
d[i]=a[i]*100.0-l*b[i];
sort(d+1,d+n+1,dcmp);
double sum=0;
for(int i=1;i<=k;++i)
sum+=d[i];
return Sign(sum)>=0;
}
int main(){
while(~scanf("%d%d",&n,&k)){
if(!n&&!k)break;
k=n-k;
for(int i=1;i<=n;++i)scanf("%lf",&a[i]);
for(int i=1;i<=n;++i)scanf("%lf",&b[i]);
double l=0,r=100,mid;
double ans=0;
while(r-l>EPS){
mid=(l+r)/2.0;
if(check(mid)){
ans=max(ans,mid);
l=mid+EPS;
}
else r=mid-EPS;
}
PRintf("%0.0lf\n",ans);
}
}