01分数规划

这几天就水水博客把以前学过的东西整理一下,其实感觉还是比较有必要的,很多东西学了之后很快就忘了QAQ不知是不是我太蠢

https://vjudge.net/problem/POJ-2976

题意:给定A数组B数组,从中选择N-K个使得R最大,输出Round(100*R);

01分数规划问题就是给定两个数组A和B,让你找出m个i使得∑A[i]/∑B[i]最大

我们设∑A[i]/∑B[i]=L,F(L)=∑A[i]-L*∑B[i]

若F[L]大于0,则存在更大的L,否则L只能减小,所以我们二分这个L

【法一】 直接二分L,算出每个i的A[i]-L*B[i],排序后取最大的m个,若和大于0则l=mid,否则r=mid;

//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
const int maxn=1005;
int ans,n,k;
double sum,mid,l=1e9,r=-1e9,eps=1e-7;
struct node{
    double a,b,d;
    friend bool operator <(const node&A,const node&B){
       return A.d<B.d;
    }
}N[maxn];
using namespace std;
int check(double L){
    sum=0;
    for(int i=1;i<=n;i++) N[i].d=N[i].a-L*N[i].b;
    sort(N+1,N+n+1);
    for(int i=k+1;i<=n;i++)
    sum+=N[i].d;
    return sum>0;
}
int main()
{
    for(;;){
      scanf("%d%d",&n,&k);
      if(!n&&!k) break;
      for(int i=1;i<=n;i++) scanf("%lf",&N[i].a);
      for(int i=1;i<=n;i++) scanf("%lf",&N[i].b);
      for(int i=1;i<=n;i++){
          l=min(l,N[i].a/N[i].b);
          r=max(r,N[i].a/N[i].b);
      }
      while(r-l>eps){
        mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
      }
      ans=floor(l*100+0.5);
      printf("%d\n",ans);
    }
   return 0;
}
二分 01分数规划

 

【法二】Dinkelbach 

一种神奇的算法,感觉学一次忘一次,今天又粗略的看了一下,就随便口胡一下。

我们先随便确定一个L,然后求出一个F(L),若它大于0,那么证明F(L)是一个比L更优的解,那么我们直接把解移到F(L)上去。这样一直移动直到达到精度要求。

//Twenty
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
const int maxn=1005;
int an,n,k;
double x,y,ans,L,sum,mid,l=1e9,r=-1e9,esp=1e-7;
struct node{
    double a,b,d;
    friend bool operator <(const node&A,const node&B){
       return A.d<B.d;
    }
}N[maxn];
using namespace std;
void work(){
   ans=0;
   for(;;){
    x=0;y=0;
       for(int i=1;i<=n;i++) N[i].d=N[i].a-ans*N[i].b;
       sort(N+1,N+1+n);
       for(int i=k+1;i<=n;i++) x+=N[i].a,y+=N[i].b;
       L=x/y;
       if(fabs(L-ans)<=esp) return;
       ans=L;
   }   
}
int main()
{
    for(;;){
      scanf("%d%d",&n,&k);
      if(!n&&!k) break;
      for(int i=1;i<=n;i++) scanf("%lf",&N[i].a);
      for(int i=1;i<=n;i++) scanf("%lf",&N[i].b);
      work(); 
      an=floor(ans*100+0.5);
      printf("%d\n",an);
    }
   return 0;
}
Dinkelbach

 

转载于:https://www.cnblogs.com/Achenchen/p/7473724.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值