题意:给定n个点m条边,一开始这些边全都是断的,要修一些边使得n个点全部联通。修完一共可以得到F元,修一条边有成本di和时间ti,要使得 得到的钱数 / 总时间 这个比值最大。

参考资料:

红线内的内容转载自http://www.cnblogs.com/scau20110726/archive/2012/10/19/2730896.html

------------------------------------------------------------------------------------------------------------------

****************************************************

解法之一 0-1分数规划

设x[i]等于1或0, 表示边e[i]是否属于生成树.

则我们所求的比率 r = ∑(benifit[i] * x[i]) / ∑(cost[i] * x[i]), 0≤i<m .

为了使 r 最大, 设计一个子问题---> 让 z = ∑(benifit[i] * x[i]) - l * ∑(cost[i] * x[i]) = ∑(d[i] * x[i]) 最大 (d[i] = benifit[i] - l * cost[i]) , 并记为z(l). 我们可以兴高采烈地把z(l)看做以d为边权的最大生成树的总权值.

然后明确两个性质:

 1.  z单调递减

  证明: 因为cost为正数, 所以z随l的减小而增大.

 2.  z( max(r) ) = 0

  证明: 若z( max(r) ) < 0, ∑(benifit[i] * x[i]) - max(r) * ∑(cost[i] * x[i]) < 0, 可化为 max(r) < max(r). 矛盾;

          若z( max(r) ) >= 0, 根据性质1, 当z = 0 时r最大.

到了这个地步, 七窍全已打通, 喜欢二分的上二分, 喜欢Dinkelbach的就Dinkelbach.

****************************************************

已知一个完全图,每条边有两个参数(dis和c),求一棵生成树,使(∑xi×ci)/(∑xi×disi)最小,其中xi当第i条边包含在生成树中时为1,否则为0。
迭代法:
假设rate为当前比率,以ci-rate×disi作为各边的权重,使用Prim算法构造最小生成树,再对该最小生成树求(∑xi×ci)/(∑xi×disi)更新rate,可证明rate可收敛且收敛值即为所求。
二分法:
在一个精度范围内(以1e-6为例),二分查找[0,maxRate]之间的值rate,使(z=∑xi×ci-rate×∑xi×disi)==0,可证明该值即为所求。其中xi为以ci-rate×disi作为各边权重时,使用Prim算法所构造的最小生成树。在二分搜索过程中若z>0,则将区间上移(low=mid+1),否则将区间下移(high=mid-1)。
使用迭代法,以0作为初始迭代比率:188MS
使用二分法,固定查找范围为[0,31],精度为1e-6:1422MS(不知二分法大家都有什么优化,分享一下吧^_^)

下面介绍一下该题目的解题思路及相关证明:

1、问题转化:

给定一个rate,z(rate)=∑xi×ci-rate*∑xi×disi,xi为一棵生成树使(∑xi×ci-rate*∑xi×disi)的值最小(下面会介绍求此生成树的方

法),则rate=(∑xi×ci-z(rate))/( ∑xi×disi),令rateNex=(∑xi×ci)/( ∑xi×disi)。

若z(rate)>0,则肯定不存在一棵生成树使rate=(∑yi×ci)/( ∑yi×disi),即rate值无效,且有rateNex >rate;

若z(rate)<0,则我们可得到一个有效比率rateNex,且rateNex<rate;

若z(rate)==0,则rateNex==rate,且rate有效。

因此,如果有且仅有一个rate使z(rate)==0,又由题目所求的最小比率的存在性(有限节点的完全图其生成树只有有限个)可知,该rate值即为所求。

下面证明其存在性和唯一性:

存在性:对于题目所求的最小比率rateMin,由上面的分析必有z(rateMin)=0,又由该最小比率的存在性可知,至少存在一个有效的比率rate使z(rate)=0;

唯一性:只要证明z(rate)函数的单调性即可:

对于两个比率rate1<rate2,

z(rate1)-z(rate2)= (∑xi×ci-rate1*∑xi×disi)-(∑yi×ci-rate2*∑yi×disi)

<=(∑yi×ci-rate1*∑yi×disi)-(∑yi×ci-rate2*∑yi×disi)

=(rate2-rate1)* ∑yi×disi>0

因此,z(rate)为单调递减函数。从而也就证明了满足z(rate)==0的比率rate的唯一性。

由上面的分析,我们就将问题转化为求解比率rate使z(rate)==0

2、求解:有两种方法对该问题进行求解:迭代法和二分法。

 A、迭代法:由上面的分析可知:

当 z(rate)>0时,rate值无效,而rateNex有效且z(rateNex)<=0;

当z(rate)<0时,rateNex<rate,又z(rate)为单调递减函数,故0>=z(rateNex)>z(rate)。

故迭代过程向z(rate)==0收敛,又由有限节点的完全图其生成树只有有限个可知迭代次数必为有限值。

 B、二分法:在定出一个搜索范围和精度之后(以[0,MAXRATE]为例),我们就可以使用二分法进行搜索:对于当前的搜索区间[low,high],mid=(low+high)/2,根据z(mid)的值及z(rate)的增减性,对搜索区间进行更新:
                   若z(mid)>0,上调区间,使low=mid+1;

若z(mid)<0,下调区间,使high=mid-1;

从而在经过有限次搜索之后便能找到所求比率。

3、求解生成树xi使(∑xi×ci-rate*∑xi×disi)的值最小的方法:

∑xi×ci-rate*∑xi×disi=∑xi(ci+rate×disi),从而问题转化为以ci+rate×disi为边的权重,求解最小生成树,对于完全图,可使用

prim算法,其复杂度只与节点数有关。

******************************************************************

简单来说(迭代的不是二分)

1.先设比率r=0,对于每条边,我们计算一个新的权值,权值为w[i][j]=c[i][j]-r*d[i][j]  (其中c[i][j]为第i个点和第j个点的垂直高度差,d[i][j]为水平距离,w[i][j]为计算出来的权值)

2.以这个权值去构建最小生成树,用prim算法(时间复杂度只与顶点数有关)去构建。最后统计这个MST的垂直高度差的和sumc,水平距离的和sumd,算出新的比率为R=sumc/sumd;

3.判断新的比率和R和旧的比率r是否相等(精度范围内,这里设为0.00001),如果相等那么R就是答案,否则就r=R(迭代),然后再次去做(1),依次循环直到找到答案

 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  
 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std; const int N=*,M=*;
const double INF=(double)1e9;
int n,m,len,first[N],fa[N];
bool vis[N];
double F;
struct node{
int x,y,next;
double w,d,t;
bool in;
}a[M]; struct cmpp
{
bool operator () (int &x,int &y)
{
return a[x].w<a[y].w;
}
}; priority_queue<int,vector<int>,cmpp> q; void ins(int x,int y,double d,double t)
{
a[++len].x=x;a[len].y=y;a[len].d=d;a[len].t=t;
a[len].next=first[x];first[x]=len;
} int findfa(int x)
{
if(fa[x]==x) return fa[x];
return findfa(fa[x]);
} double myabs(double x){return x> ? x:-x;} bool cmp(node x,node y){return x.w>y.w;} bool check(double r)
{
int cnt=;
double sum=F;
while(!q.empty()) q.pop();
memset(vis,,sizeof(vis));
for(int i=;i<=len;i++)
{
a[i].w=-a[i].d-r*a[i].t;
a[i].in=;
}
vis[]=;
for(int i=first[];i;i=a[i].next) a[i].in=,q.push(i); while(cnt!=n-)
{
int ind;
while()
{
ind=q.top();q.pop();
int x=a[ind].x,y=a[ind].y;
if(!vis[x])
{
for(int i=first[x];i;i=a[i].next) if(!a[i].in) a[i].in=,q.push(i);
break;
}
if(!vis[y])
{
for(int i=first[y];i;i=a[i].next) if(!a[i].in) a[i].in=,q.push(i);
break;
}
}
cnt++;
vis[a[ind].x]=vis[a[ind].y]=;
sum+=a[ind].w;
}
return (sum>=);
} int main()
{
freopen("a.in","r",stdin);
// freopen("quake.in","r",stdin);
// freopen("quake.out","w",stdout); scanf("%d%d%lf",&n,&m,&F);
len=;
memset(first,,sizeof(first));
for(int i=;i<=m;i++)
{
int x,y;
double d,t;
scanf("%d%d%lf%lf",&x,&y,&d,&t);
ins(x,y,d,t);
ins(y,x,d,t);
} double l=,r=INF,mid;
while(l<r)
{
mid=(l+r)/;
if(check(mid)) l=mid;
else r=mid;
if(myabs(l-r)<=0.000001) break;
}
printf("%.4lf\n",r);
return ;
}

【usaco-Earthquake, 2001 Open】 0-1分数规划 & 最优比率生成树的更多相关文章

  1. poj2728 Desert King(最小生成树+01分数规划=最优比率生成树)

    题意 n个点完全图,每个边有两个权值,求分数规划要求的东西的最小值. (n<=1000) 题解 心态炸了. 堆优化primT了. 普通的就过了. 我再也不写prim了!!!! 咳咳 最优比率生成 ...

  2. 【Earthquake, 2001 Open 】 0-1 分数规划

    71  奶牛施工队一场地震把约翰家园摧毁了,坚强的约翰决心重建家园.约翰已经修复了 N 个牧场,他需要再修复一些道路把它们连接起来.碰巧的是,奶牛们最近也成立了一个工程队,专门从事道路修复.而然,奶牛 ...

  3. poj 2976 Dropping tests 0/1分数规划

    0/1分数规划问题,用二分解决!! 代码如下: #include<iostream> #include<stdio.h> #include<algorithm> # ...

  4. bzoj 3597: [Scoi2014]方伯伯运椰子 0/1分数规划

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 144  Solved: 78[Submit][Status ...

  5. LOJ 3089 「BJOI2019」奥术神杖——AC自动机DP+0/1分数规划

    题目:https://loj.ac/problem/3089 没想到把根号之类的求对数变成算数平均值.写了个只能得15分的暴力. #include<cstdio> #include< ...

  6. poj2728 Desert King【最优比率生成树】【Prim】【0/1分数规划】

    含[最小生成树Prim]模板. Prim复杂度为$O(n^2),适用于稠密图,特别是完全图的最小生成树的求解.   Desert King Time Limit: 3000MS   Memory Li ...

  7. POJ - 2976 Dropping tests && 0/1 分数规划

    POJ - 2976 Dropping tests 你有 \(n\) 次考试成绩, 定义考试平均成绩为 \[\frac{\sum_{i = 1}^{n} a_{i}}{\sum_{i = 1}^{n} ...

  8. [SDOI2017]新生舞会 0/1分数规划

    ---题面--- 题解: 0/1分数规划,,,但是竟然有诡异的精度问题???因为这个被卡了好久 中途还写过一次KM,,,结果陷入死循环,,,我大概是写了一个假KM,,,于是放弃KM,回来调费用流 这个 ...

  9. bzoj3232圈地游戏——0/1分数规划+差分建模+判环

    Description DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用. DZY喜欢在地里散步.他总是从任意一个格点出发,沿着格线行走直到 ...

随机推荐

  1. C++ 学习笔记之 引用

    一.定义: 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 二.用法: 基本用法 例如: int & a = b; 引用作为函数返回值 先看一个例子: #inclu ...

  2. UBUNTU如何安装tar.gz版的flash

    adobe flash player的官方下载页面为:https://get.adobe.com/cn/flashplayer/ 不过近期通过APT方式以及ubuntu的软件中心都安装不了flashp ...

  3. iOS- 无处不在,详解iOS集成第三方登录(SSO授权登录<无需密码>)

    1.前言   不多说,第三登录无处不在!必备技能,今天以新浪微博为例. 这是上次写的iOS第三方社交分享:http://www.cnblogs.com/qingche/p/3727559.html 可 ...

  4. Zigbee安全基础篇Part.2

    原文地址: https://www.4hou.com/wireless/14252.html 导语:本文将会探讨ZigBee标准提供的安全模型,用于安全通信的各种密钥.ZigBee建议的密钥管理方法以 ...

  5. 微信小程序 功能函数 计时器

    let lovetime = setInterval(function () { let str = '(' + n + ')' + '重新获取' that.setData({ getText2: s ...

  6. 关于new delete的说明

    1. 删除空指针不会有问题,因为C++的标准规定在delete时首先会判断指针是否为空,为空就不再处理,所以也就不会有问题. 2. delete一个非空指针之后,并不会将该指针自动置为空.此时如果重复 ...

  7. Spring Boot 最简单的HelloWorld

    创建一个Spring Boot,可以直接使用构建工具(Maven或Gradle)创建,也可以使用spring.io网站创建,一般会选择使用spring.io创建 使用IDEA创建一个Spring Bo ...

  8. Runtime介绍

    本文目录 1.Runtime简介 2.Runtime相关的头文件 3.技术点和应用场景 3_1.获取属性\成员变量列表 3_2.交换方法实现 3_3.类\对象的关联对象,假属性 3_4.动态添加方法, ...

  9. Gevent-自动挡切换

    Gevent: Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程. G ...

  10. 锁-lock,信号量4

    1. 全局解释器锁,保证同一时间只有一个线程在执行,但是由于它是把数据copy成了两份,所以 只有全局解释器锁的时候,数据加减照样出错了. 2.用户态的锁,保证同一时间,只有一个线程在真真正正地修改数 ...