POJ-2976 Dropping tests(01分数规划入门)

来源:互联网 发布:win10软件找不到了 编辑:程序博客网 时间:2024/05/20 14:20

01分数规划问题:

所谓的01分数规划问题就是指这样的一类问题,给定两个数组,a[i]表示选取i的收益,b[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得R=sigma(a[i]*x[i])/sigma(b[i]*x[i])取得最值,即所有选择物品的总收益/总代价的值最大或是最小。


分析(详):

大体概括一下,就是定义一个函数F(L) = sigma(a[i]*x[i])-L*sigma(b[i]*x[i]),其实就是将R换为自变量L后上述式子的变形,分离参数,得到F(L)=sigma((a[i]-L*b[i])*x[i])。如果我们固定L在某个方案下能导致F(L)大于0,这代表存在更优的L,所以我们不断变换L,寻找一个临界L使得不存在一种方案,能够使F(L)>0。另d[i]=(a[i]-L*b[i]),显然d数组是随着L的增大而单调减的,所以根据d数组去判断边界,如果d数组的相应方案的和>=0则表明可能存在更优解,否则不存在。

所以根据性质,很自然想到用二分去求解。二分的下界一般都是取为0(a[i],b[i]一般都是大于0的),但是上界怎么求呢?假设两个方案a1,b1和a2,b2。它们分别的比率是a1/b1,a2/b2,组合的比率是(a1+a2)/(b1+b2)

a1/b1 - (a1+a2)/(b1+b2)化简后是(a1b2-a2b1)/(b1(b1+b2))

a2/b2 - (a1+a2)/(b1+b2)化简后是(a2b1-a1b2)/(b2(b1+b2))

由于正负取决于分子,所以可以看出两者组合的比率必定小于其中一个单独的比率。所以上界就是寻找一个最大的单独比率。

二分是一个非常通用的办法,但是二分的时候我们只是用到了F(L)>0这个条件,而对于使得F(L)>0的这组解所求到的R值没有使用。因为F(L)>0,我们已经知道了R是一个更优的解,与其漫无目的的二分,为什么不将解移动到R上去呢?求01分数规划的另一个方法就是Dinkelbach算法,它就是基于这样的一个思想,他并不会去二分答案,而是先随便给定一个答案,然后根据更优的解不断移动答案,逼近最优解。由于对每次判定使用的更加充分,所以它比二分会快上很多。但是,它的弊端就是需要保存这个解,而我们知道,有时候验证一个解和求得一个解的复杂度是不同的。二分和Dinkelbach算法写法都非常简单,各有长处,要根据题目谨慎使用。


POJ-2976题意:

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

代码1(二分方法 79ms):

#include <algorithm>#include <cstdio>#define LL long longusing namespace std;const int maxn = 1005;const double eps = 1e-6;LL a[maxn], b[maxn];double d[maxn];int n, k;LL _round(double x){if(x-(LL)x < 0.5) return (LL)x;else return (LL)x+1;}bool jg(double v){double tmp = 0;for(int i = 1; i <= n; ++i)d[i] = a[i]-b[i]*v;sort(d+1, d+n+1);for(int i = 0; i < k; ++i)tmp += d[n-i];return tmp >= 0;}void work(){double l = 0, r = 0, mid;for(int i = 1; i <= n; ++i)if(1.0*a[i]/b[i] > r) r = 1.0*a[i]/b[i];while(r-l >= eps){mid = (l+r)/2.0;if(jg(mid)) l = mid;else r = mid;}printf("%lld\n", _round(l*100));}int main(){while(scanf("%d %d", &n, &k), (n||k)){for(int i = 1; i <= n; ++i)scanf("%lld", &a[i]);for(int i = 1; i <= n; ++i)scanf("%lld", &b[i]);k = n-k; work();}return 0;}


代码2(Dinkelbach算法 32ms)

#include <algorithm>#include <cstdio>#include <cmath>#define LL long longusing namespace std;const int maxn = 1005;const double eps = 1e-6;struct node{int a, b;double d;} p[maxn];int cmp(node x, node y) {return x.d < y.d;}int n, k;LL _round(double x){if(x-(LL)x < 0.5) return (LL)x;else return (LL)x+1;}void work(){double ans = 1.0, tmp, x, y;do{tmp = ans;for(int i = 1; i <= n; ++i)p[i].d = p[i].a-p[i].b*tmp;sort(p+1, p+n+1, cmp);x = y = 0.0;for(int i = 0; i < k; ++i)x += p[n-i].a, y += p[n-i].b;ans = x/y;}while(fabs(tmp-ans) >= eps);printf("%lld\n", _round(ans*100));}int main(){while(scanf("%d %d", &n, &k), (n||k)){for(int i = 1; i <= n; ++i)scanf("%d", &p[i].a);for(int i = 1; i <= n; ++i)scanf("%d", &p[i].b);k = n-k; work();}return 0;}


继续加油~

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 信而富本时段额度已放完怎么办 知道qq号密码账号忘了怎么办 扣扣绑定的手机号被别人用了怎么办 我的手机号被别人绑定了快手怎么办 微信密码忘记了没绑定手机号怎么办 扣扣绑定银行卡忘记密码了怎么办 扣扣忘记密码了又换手机号了怎么办 微信订阅号里有删除后的信息怎么办 申请微信公众号邮箱已被占用怎么办 公众号验证没有对公账户怎么办 qq密码忘记了怎么办手机绑定没有 我的扣扣怎么申诉都找不回来怎么办 装修装的不好又没有签合同怎么办 日本免税的零食不小心拆了怎么办 闲鱼同意买家退货了买家不退怎么办 手机里淘宝钱付了不发货怎么办 百度云下载手机储存空间不足怎么办 苹果8的照片储存空间满了怎么办 为什么下载的软件已停止运行怎么办 苹果手机刷机忘记注册邮箱了怎么办 刺激战场模拟器注册已达上限怎么办 用模拟器玩刺激战场注册上限怎么办 微信解除实名认证后退款怎么办 苹果为什么qq收消息有延迟怎么办 qq忘记密码手机号也换了怎么办 扣扣空间圈人时照片服务错误怎么办 删了qq好友怎么找回来怎么办 苹果5s语音控制打开了怎么办 lv迷你水桶包肩带长了怎么办 在香港买个lv包包过海关怎么办 国际快递手表被海关查应该怎么办 把档案放到人才市场后报到证怎么办 皮表带带久了有异味怎么办 英语中用词不当和拼写错误怎么办 爬楼梯的购物车车轮坏了怎么办? 帮别人买东西不给我钱怎么办 老师念错名字有同学指出来你怎么办 老公婚前买的房子婆婆想霸占怎么办 我想查我的基金收益情况怎么办 儿童票买好了但大人退票了怎么办 没有享受到国家政策的农民怎么办?