【usaco-Earthquake, 2001 Open】 0-1分数规划 & 最优比率生成树
题意:给定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分数规划 & 最优比率生成树的更多相关文章
- poj2728 Desert King(最小生成树+01分数规划=最优比率生成树)
题意 n个点完全图,每个边有两个权值,求分数规划要求的东西的最小值. (n<=1000) 题解 心态炸了. 堆优化primT了. 普通的就过了. 我再也不写prim了!!!! 咳咳 最优比率生成 ...
- 【Earthquake, 2001 Open 】 0-1 分数规划
71 奶牛施工队一场地震把约翰家园摧毁了,坚强的约翰决心重建家园.约翰已经修复了 N 个牧场,他需要再修复一些道路把它们连接起来.碰巧的是,奶牛们最近也成立了一个工程队,专门从事道路修复.而然,奶牛 ...
- poj 2976 Dropping tests 0/1分数规划
0/1分数规划问题,用二分解决!! 代码如下: #include<iostream> #include<stdio.h> #include<algorithm> # ...
- bzoj 3597: [Scoi2014]方伯伯运椰子 0/1分数规划
3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec Memory Limit: 64 MBSubmit: 144 Solved: 78[Submit][Status ...
- LOJ 3089 「BJOI2019」奥术神杖——AC自动机DP+0/1分数规划
题目:https://loj.ac/problem/3089 没想到把根号之类的求对数变成算数平均值.写了个只能得15分的暴力. #include<cstdio> #include< ...
- poj2728 Desert King【最优比率生成树】【Prim】【0/1分数规划】
含[最小生成树Prim]模板. Prim复杂度为$O(n^2),适用于稠密图,特别是完全图的最小生成树的求解. Desert King Time Limit: 3000MS Memory Li ...
- POJ - 2976 Dropping tests && 0/1 分数规划
POJ - 2976 Dropping tests 你有 \(n\) 次考试成绩, 定义考试平均成绩为 \[\frac{\sum_{i = 1}^{n} a_{i}}{\sum_{i = 1}^{n} ...
- [SDOI2017]新生舞会 0/1分数规划
---题面--- 题解: 0/1分数规划,,,但是竟然有诡异的精度问题???因为这个被卡了好久 中途还写过一次KM,,,结果陷入死循环,,,我大概是写了一个假KM,,,于是放弃KM,回来调费用流 这个 ...
- bzoj3232圈地游戏——0/1分数规划+差分建模+判环
Description DZY家的后院有一块地,由N行M列的方格组成,格子内种的菜有一定的价值,并且每一条单位长度的格线有一定的费用. DZY喜欢在地里散步.他总是从任意一个格点出发,沿着格线行走直到 ...
随机推荐
- Java中的基本数据类型包装类
在 java 中为什么会有基本数据类型的包装类? ①:基本数据类型之间的相互转换不是都可以制动转换的,而你强制转换又会出问题,比如String类型的转换为int类型的,那么jdk为了方便用户就提供了相 ...
- 团队作业7——第二次项目冲刺(Beta版本)
团队作业7——第二次项目冲刺-Beta版本项目计划 团队作业7——第二次项目冲刺(Beta版本)-第一篇 团队作业7——第二次项目冲刺(Beta版本)-第二篇 团队作业7——第二次项目冲刺(Beta版 ...
- OSG学习:矩阵变换节点示例
#include<osgViewer\Viewer> #include<osg\Node> #include<osg\Geode> #include<osg\ ...
- 在linux下如何显示隐藏文件
#显示所有文件(包含隐藏文件)ls -a #只显示隐藏文件l.或者ls -d .* #在XWindow的KDE桌面中在"查看(View)"菜单里选"显示隐藏文件(Show ...
- 解决连接mysql报错1130
最近在服务器上部署好的应用突然间连接不上mysql数据库,报错“ERROR 1130: Host xxx.xxx.xxx.xxx is not allowed to connect to this M ...
- 织梦CMS建站入门学习(一)
一.下载与安装. 首先到织梦官网下载软件,可选择UTF8或GBK不同编码格式,如果电脑没有PHP环境,还要下载dede自带的PHP环境软件. 将软件中的upload文件夹内容复制到WWW文件夹下,然后 ...
- RT-thread内核之对象管理系统
一.数据结构 1.对象控制块:在include/rtdef.h中定义 /** * Base structure of Kernel object */ struct rt_object { char ...
- AC自动机裸题
HDU 2222 Keywords Search 模板题.对模式串建立AC自动机然后在trie树上找一遍目标串即可. # include <cstdio> # include <cs ...
- BZOJ 1191 超级英雄(二分图匹配)
把题目作为s集,锦囊作为t集.把每个题目和它可以用的锦囊连边,这样就构成了一个二分图,求出这个二分图最大匹配. 但是这个最大匹配有限制条件,就是对于每个可能的匹配集,如果s集的i点有匹配,那么i-1点 ...
- Socket网络编程实例2
两个程序通过“网络”交互数据就使用socket,它只负责两件事:建立连接,传递数据. 所有的数据传输接收,必须都使用byte格式 1.简单实例: #客户端 import socket client=s ...