NYIST 914Yougth的最大化【二分搜索/Dinkelbach算法】
转载请注明出处:http://www.cnblogs.com/KirisameMarisa/p/4187637.html
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=914
问题描述:有N个物体,它们的利益用v[i]表示,代价用c[i]表示。现在要在这N个物体中选取K个物体,使得选出来的这K个物体的总利益除以总代价达到最大值。即取得最大值。
问题转化:构造一个x[N]的数组,表示每个数取或不取的状态,显然每一个x[i]只有两个取值:0和1,其中1表示取,0表示不取。则整个式子也就可以变成目标式:
值得注意的是:上式中的r是每一组x对应求得的当前答案,而我们的目的就是要找到一组x使得求出来的r得到最大值R!(这很重要!)
一、二分法
进一步对式子进行处理:
简单的一项处理:
构造一个函数:
其中F(r)在平面坐标系上体现为一条直线,每一组x[i]都分别唯一地对应一条直线,这些直线的截距均大于等于0、斜率均小于等于0。而这些直线在X轴上的截距就是这一组x求出来的r,而截距的最大值就是我们要求的R。(如下图所示)
在X轴上面任取一个r,如果至少有一条直线的F(r)>0,那么说明了什么呢?
说明至少还有一条直线与X轴的交点在它的右边,那么这个r一定不是最大值,真正的最大值在它的右边。反之,如果所有的F(r)都小于0,那么真正的最大值在它的左边
那么前面的结论就可以换种说法,因为我只需判断最大的那个F(r)的正负性就行了:
随便取一个r,如果F(r)max>0,则结果R>r,反之若F(r)max<0,则结果R<r。直到找到使F(r)max=0的r,那个r就是我们要找的结果R
显然这是一个令人感到兴奋的结论,因为我们自然而然就想到了一种方法:二分法!
至此,还有一个小小的问题没有解决,那就是F(r)max怎么求?
至此,问题全部解决!
#define eps 1e-8
#define zero(a) fabs(a)<eps double v[MAXN],c[MAXN],d[MAXN]; int main()
{
int N,K;
scanf("%d%d",&N,&K);
for(int i=;i<N;i++)
scanf("%lf%lf",&v[i],&c[i]);
double l=0.0,r=10000000.0;
while(!(zero(r-l))) //二分终止条件,利用double的精度控制
{
double mid=(r+l)/2.0;
for(int i=;i<N;i++)
d[i]=v[i]-mid*c[i];
sort(d,d+N);
double sum=0.0;
for(int i=N-;i>=N-K;i--)
sum+=d[i];
if(sum>=) l=mid;
else r=mid;
}
double ans=l;
printf("%.2lf\n",ans);
return ;
}
二分法可以轻松水过南阳理工学院oj上的原题和ghbgh出的上大oj上的改题甚至是POJ的2976这些最基本的0-1分数规划问题。不过,用这种方法去做POJ上面的3111时却直接TLE了orz。。。。
二分是一个非常通用的办法,但是我们来考虑这样的一个问题,二分的时候我们只是用到了F(r)>0这个条件,而对于使得F(r)>0的这组解所求到的R值没有使用。因为F(r)>0,我们已经知道了R是一个更优的解,与其漫无目的的二分,为什么不将解移动到R上去呢?
我们再次回到函数表达图:
这个是二分的原理表现,在图中,我们取左界为0,右界为足够大,画出中间的(左+右)/2,发现此时F(r)max是大于0的,那么继续将左界移向(左+右)/2,继续二分,直到找到R为止。
但是我们换种思路,我们在判断一个当前的r的时候需要去求一个F(r)max,在二分之中我们仅仅判断了F(r)max与0的关系,这是利用率比较低的。其实我们可以将F(r)max利用起来。找到F(r)max所在的那一条直线,然后将r移动到这条直线的截距上面去(如下图,找到当前的F(r)max所在的直线,将r移动到r4上面去,这样做甚至只要2步即可到位)
这就是下面要介绍的方法:
二、Dinkelbach算法
它实质上是一种迭代,他是基于这样的一个思想:他并不会去二分答案,而是先随便给定一个答案,然后根据更优的解不断移动答案,逼近最优解。由于他对每次判定使用的更加充分,所以它比二分会快上很多。
在这个算法中,我们可以将r初始化为任意值,不过由于所有直线都只有y轴右边的部分,所以一般将r初始化为0。
double m[+],w[+];
struct node{double num;int ord;} d[+];
bool cmp(node a,node b){return a.num>b.num;}
int main()
{
int N,K;
scanf("%d%d",&N,&K);
for(int i=;i<N;i++)
scanf("%lf%lf",&m[i],&w[i]);
double l=0.0,ans;
while()
{
ans=l;
for(int i=;i<N;i++)
{
d[i].num=m[i]-ans*w[i];
d[i].ord=i;
}
sort(d,d+N,cmp);
double p=0.0,q=0.0;
for(int i=;i<K;i++)
{
p+=m[d[i].ord];
q+=w[d[i].ord];
}
l=p/q;
if(zero(ans-l))
break;
}
printf("%.2f\n",ans);
return ;
}
不过需要注意的是,并不是可以放弃二分全用Dinkelbach算法。这只是最基本的0-1规划问题, Dinkelbach算法的弊端就是需要保存解。在更加复杂的问题中,有的时候二分更快,有时Dinkelbach算法更快。
二分和Dinkelbach算法写法都非常简单,各有长处,大家要根据题目谨慎使用。
by---Kirisame_Marisa 2014-12-26 22:31:43
NYIST 914Yougth的最大化【二分搜索/Dinkelbach算法】的更多相关文章
- [poj2976]Dropping tests(01分数规划,转化为二分解决或Dinkelbach算法)
题意:有n场考试,给出每场答对的题数a和这场一共有几道题b,求去掉k场考试后,公式.的最大值 解题关键:01分数规划,double类型二分的写法(poj崩溃,未提交) 或者r-l<=1e-3(右 ...
- 01分数规划问题(二分法与Dinkelbach算法)
链接 前置技能 二分思想 最短路算法 一些数学脑细胞? 问题模型1基本01分数规划问题给定n个二元组(valuei,costi),valuei是选择此二元组获得的价值(非负),costi是选择此二元组 ...
- POJ 3258 最小值最大化 二分搜索
题意:牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离, 现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值. ...
- poj Dropping tests 01分数规划---Dinkelbach算法
果然比二分要快将近一倍.63MS.二分94MS. #include <iostream> #include <algorithm> #include <cstdio> ...
- 【poj 2976】Dropping tests(算法效率--01分数规划 模版题+二分){附【转】01分数规划问题}
P.S.又是一个抽时间学了2个小时的新东西......讲解在上半部分,题解在下半部分. 先说一下转的原文:http://www.cnblogs.com/perseawe/archive/2012/05 ...
- 聚类算法之BIRCH(Java实现)转载
http://www.cnblogs.com/zhangchaoyang/articles/2200800.html http://blog.csdn.net/qll125596718/article ...
- 《编程珠玑》第二章 aha算法
A题:给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数. 1.在文件中至少存在这样一个数? 2.如果有足够的内存,如何处理? 3.如果内存不足,仅可以用文件来进行 ...
- C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】
C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...
- EM算法及其应用(一)
EM算法及其应用(一) EM算法及其应用(二): K-means 与 高斯混合模型 EM算法是期望最大化 (Expectation Maximization) 算法的简称,用于含有隐变量的情况下,概率 ...
随机推荐
- 延迟N秒执行某个方法
[self performSelector:<#(nonnull SEL)#> withObject:<#(nullable id)#> afterDelay:<#(NS ...
- Vector(容器)
vector(容器)就像数组一样,但比数组强大很多,下面介绍一下vector常用的几种方法: 一.对于vector自身的处理,包括赋初始值,复制等等: vector<int> v1 ; v ...
- INDEX相关
1.索引应该建立在WHERE子句经常用到的表列上,如果在大表上频率使用某列或者某几列作为条件执行检索操作,并且检索的行数低于总行数的15%,那么应该考虑在该几行上添加索引. 2.为了提高多表连接的性能 ...
- selenium 学习笔记 ---新手学习记录(5) 问题总结(java)
1.今天遇到个奇葩问题,iframe有两个id相同的(如下图) 使用driver.switchTo().frame(“frmLinkPage1”);这个无法使用了. 后来改用driver.switch ...
- c语言各类问题 代码
定义一个结构体,有两个成员变量,一个整型的n,一个字符型的c,利用结构体类型声明一个具有5个元素的数组,并随机初始化,根据成员变量n进行从小到大排序,然后输出 冒泡排序然后 在输出结构体#includ ...
- ie6与固定定位fixed,+ 条件注释格式注意
ie6并不支持position:fixed, ie7+都支持fixed定位, ie6固定定位实现方法1: <!DOCTYPE html> <html> <head> ...
- Linux计时体系结构
[Linux操作系统分析]定时测量——RTC,TSC,PIT,jiffies,计时体系结构,延迟函数 1 基本概念 定时机制连同一些更可见的内核活动(如检查超时)来驱使进程切换. 两种主要的定时测 ...
- PHP查看在线服务器与本地服务器支持函数差别
在本地开发好的程序.代码,当传到服务器后,经常会出现罢工的事情.这时候就要考虑开发环境和在线的环境是否完全一致了.这里所说的一致性,包括了容器的配置.php的配置等等.下面所要解决的只是其中一项,你的 ...
- 在cnblog中使用syntax方法
<pre name="code" class="brush: cpp;"> 代码 </pre> #include<cstdio&g ...
- HDU 2717 Catch That Cow
简单的广搜: #include <cstdio> #include <queue> using namespace std; ],step[]; int n,start,end ...