【BZOJ3597】方伯伯运椰子(分数规划,网络流)

题解

给定了一个满流的费用流模型

如果要修改一条边,那么就必须满足流量平衡

也就是会修改一条某两点之间的路径上的所有边

同时还有另外一条路径会进行相反的修改

现在要求最大化\(\frac{X-Y}{K}\)

二分答案\(mid\)

式子变为\(X-Y-K·mid\geq 0\)

换而言之,相当于给每次修改操作额外付出一个代价\(mid\)

要使得费用+修改代价最小

对于扩容我们很好处理

对于每条边再额外连一条边

容量为\(inf\)(可以无限扩容),费用为扩容的费用,同时,每次流过还会产生一个费用

所以扩容的费用是\(b+d\)

但是压缩呢?

对于每次压缩,相当于是退流了

所以,我们给反边的费用额外增加一个压缩的费用

所以这里的费用是\(a-d\)

但是,似乎还是不会做?

我们虽然这样处理了,但是不知道怎么计算结果。

在看了天哥的博客后,我发现了这题真的很妙啊

我们先假设所有的边都被你压缩成零了,产生了一定的费用,这个可以直接算贡献。

接下来我们有两种边

一种是反压缩,我们把压缩的容量给还原,容量是边的容量,费用是\(d-a\)。

另外一种就是扩容,也就是额外新增容量,容量为\(inf\),费用\(b+d\)

再把分数规划给套上去,把每个费用都加上一个\(mid\)(反压缩是减)

这样给源点流量为原图中的流量,做一次费用流就好啦。

然后我们再来想一想我们的分数规划变成了求什么。

本来是让\(X-Y-K·mid\geq 0\)

也就是\(Y+K·mid-X\leq 0\)

因为提前压缩的时候我们已经把所有的流量贡献的费用给减掉了

也就是我们已经减过\(X\)了

所以就是最后费用流的结果+压缩所有边的费用之和要小于\(0\)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<set>
  8. #include<map>
  9. #include<vector>
  10. #include<queue>
  11. using namespace std;
  12. #define ll long long
  13. #define RG register
  14. #define MAX 5555
  15. #define inf 1000000000
  16. inline int read()
  17. {
  18. RG int x=0,t=1;RG char ch=getchar();
  19. while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
  20. if(ch=='-')t=-1,ch=getchar();
  21. while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  22. return x*t;
  23. }
  24. int n,m,S,T;
  25. int U[MAX],V[MAX],A[MAX],B[MAX],C[MAX],D[MAX];
  26. struct Line{int v,next,w;double fy;}e[50000];
  27. int h[MAX],cnt=2;
  28. inline void Add(int u,int v,int w,double fy)
  29. {
  30. e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
  31. e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
  32. }
  33. double sum,Sum;
  34. void Build(double mid)
  35. {
  36. memset(h,0,sizeof(h));cnt=2;sum=0;
  37. for(int i=1;i<=m;++i)
  38. {
  39. if(U[i]!=S)Add(U[i],V[i],inf,B[i]+D[i]+mid);
  40. Add(U[i],V[i],C[i],-(A[i]-D[i]+mid));
  41. sum+=C[i]*(A[i]-D[i]+mid);
  42. }
  43. }
  44. int pe[MAX],pv[MAX];
  45. double dis[MAX];
  46. bool vis[MAX];
  47. bool SPFA()
  48. {
  49. for(int i=1;i<=T;++i)dis[i]=1e18;
  50. dis[S]=0;vis[S]=true;
  51. queue<int> Q;Q.push(S);
  52. while(!Q.empty())
  53. {
  54. int u=Q.front();Q.pop();
  55. for(int i=h[u];i;i=e[i].next)
  56. {
  57. int v=e[i].v;if(!e[i].w)continue;
  58. if(dis[v]>dis[u]+e[i].fy)
  59. {
  60. dis[v]=dis[u]+e[i].fy;
  61. pv[v]=u;pe[v]=i;
  62. if(!vis[v])vis[v]=true,Q.push(v);
  63. }
  64. }
  65. vis[u]=false;
  66. }
  67. if(dis[T]>=1e18)return false;
  68. int flow=inf;
  69. for(int i=T;i!=S;i=pv[i])flow=min(flow,e[pe[i]].w);
  70. for(int i=T;i!=S;i=pv[i])e[pe[i]].w-=flow,e[pe[i]^1].w+=flow;
  71. sum+=flow*dis[T];
  72. return true;
  73. }
  74. bool check(double mid)
  75. {
  76. Build(mid);
  77. while(SPFA());
  78. return sum<0;
  79. }
  80. int main()
  81. {
  82. n=read();m=read();S=n+1;T=n+2;
  83. for(int i=1;i<=m;++i)
  84. U[i]=read(),V[i]=read(),A[i]=read(),B[i]=read(),C[i]=read(),D[i]=read();
  85. double l=0,r=5e4;
  86. while(r-l>1e-3)
  87. {
  88. double mid=(l+r)/2;
  89. if(check(mid))l=mid;
  90. else r=mid;
  91. }
  92. printf("%.2lf\n",l);
  93. return 0;
  94. }

当然,还有另外一种方法

费用流上的消圈定理:

如果残余网络上还有负环,证明当前不是最优的费用流

因此,构建出残余网络之后直接检查有无负环即可

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<algorithm>
  7. #include<set>
  8. #include<map>
  9. #include<vector>
  10. #include<queue>
  11. using namespace std;
  12. #define ll long long
  13. #define RG register
  14. #define MAX 5555
  15. inline int read()
  16. {
  17. RG int x=0,t=1;RG char ch=getchar();
  18. while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
  19. if(ch=='-')t=-1,ch=getchar();
  20. while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  21. return x*t;
  22. }
  23. struct Line{int v,next;int w;}e[MAX<<2];
  24. int h[MAX],cnt=1;
  25. inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
  26. int n,m,U,V,A,B,C,D;
  27. double dis[MAX];
  28. bool vis[MAX];
  29. bool SPFA(int u,double mid)
  30. {
  31. vis[u]=true;
  32. for(int i=h[u];i;i=e[i].next)
  33. {
  34. int v=e[i].v;
  35. if(dis[v]>dis[u]+e[i].w+mid)
  36. {
  37. dis[v]=dis[u]+e[i].w+mid;
  38. if(vis[v]||SPFA(v,mid))return true;
  39. }
  40. }
  41. vis[u]=false;
  42. return false;
  43. }
  44. bool check(double mid)
  45. {
  46. for(int i=1;i<=n+2;++i)dis[i]=0,vis[i]=false;
  47. for(int i=1;i<=n+2;++i)
  48. if(SPFA(i,mid))return true;
  49. return false;
  50. }
  51. int main()
  52. {
  53. n=read();m=read();
  54. for(int i=1;i<=m;++i)
  55. {
  56. U=read(),V=read(),A=read(),B=read(),C=read(),D=read();
  57. Add(U,V,B+D);if(C)Add(V,U,A-D);
  58. }
  59. double l=0,r=5e4;
  60. while(r-l>1e-3)
  61. {
  62. double mid=(l+r)/2;
  63. if(check(mid))l=mid;
  64. else r=mid;
  65. }
  66. printf("%.2lf\n",l);
  67. return 0;
  68. }

【BZOJ3597】方伯伯运椰子(分数规划,网络流)的更多相关文章

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

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MB Submit: 404  Solved: 249 [Submit][Sta ...

  2. [bzoj3597][scoi2014]方伯伯运椰子——分数规划,负环

    题解 目标就是 \[Maximize\ \lambda = \frac{X-Y}{k}\] 按照分数规划的一般规律, 构造: \[g(\lambda) = \lambda k + Y - X\] 由于 ...

  3. 洛谷3288 SCOI2014方伯伯运椰子(分数规划+spfa)

    纪念博客又一次爆炸了 首先,对于本题中,我们可以发现,保证存在正整数解,就表示一定费用会降低.又因为一旦加大的流量,费用一定会变大,所以总流量一定是不变的 那么我们这时候就需要考虑一个退流的过程 对于 ...

  4. bzoj3597 方伯伯运椰子

    有一个 DAG,有一个源点,一个汇点和很多条边,每条边有花费 $d_i$ 和最大流量 $c_i$,可以花 $b_i$ 的钱把最大流量增加 $1$,花 $a_i$ 的钱把最大流量减少 $1$ 现在要进行 ...

  5. bzoj3597[Scoi2014]方伯伯运椰子 01分数规划+spfa判负环

    3597: [Scoi2014]方伯伯运椰子 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 594  Solved: 360[Submit][Statu ...

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

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

  7. bzoj 3597: [Scoi2014]方伯伯运椰子 [01分数规划 消圈定理 spfa负环]

    3597: [Scoi2014]方伯伯运椰子 题意: from mhy12345 给你一个满流网络,对于每一条边,压缩容量1 需要费用ai,扩展容量1 需要bi, 当前容量上限ci,每单位通过该边花费 ...

  8. 「SCOI2014」方伯伯运椰子 解题报告

    「SCOI2014」方伯伯运椰子 可以看出是分数规划 然后我们可以看出其实只需要改变1的流量就可以了,因为每次改变要保证流量守恒,必须流成一个环,在正负性确定的情况下,变几次是无所谓的. 然后按照套路 ...

  9. BZOJ3597 SCOI2014方伯伯运椰子(分数规划+spfa)

    即在总流量不变的情况下调整每条边的流量.显然先二分答案变为求最小费用.容易想到直接流量清空跑费用流,但复杂度略有些高. 首先需要知道(不知道也行?)一种平时基本不用的求最小费用流的算法——消圈法.算法 ...

随机推荐

  1. opencv学习(1.2) - Windows 10 安装OpenCV &配置VS 2015

    windows 10 安装OpenCV&配置VS 2015 环境 系统:Windows 10 OpenCV版本:3.4.1 开发IDE:VS2015 社区版 下载安装 下载OpenCV 3.4 ...

  2. PAT 1063 Set Similarity[比较]

    1063 Set Similarity (25 分) Given two sets of integers, the similarity of the sets is defined to be N ...

  3. 记录:tensoflow改错TypeError: Cannot interpret feed_dict key as Tensor: Can not convert a float into a Te

    错误描述: TypeError: Cannot interpret feed_dict key as Tensor: Can not convert a float into a Tensor. 改错 ...

  4. python 中字典的操作(增、删、改、查)

    字典是另一种可变容器模型,且可存储任意类型对象,下标从0开始,最后一个为-1. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({}) ...

  5. WEB前端研发工程师编程能力成长之路(1)

    [背景] 如果你是刚进入WEB前端研发领域,想试试这潭水有多深,看这篇文章吧: 如果你是做了两三年WEB产品前端研发,迷茫找不着提高之路,看这篇文章吧: 如果你是四五年的前端开发高手,没有难题能难得住 ...

  6. javascript面向对象笔记(一)

    ECMAscript对象(以下简称对象): ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数. 对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.值可以是数据 ...

  7. HDU 1950 Bridging signals(LIS)

    最长上升子序列(LIS)的典型变形,O(n^2)的动归会超时.LIS问题可以优化为nlogn的算法. 定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元 ...

  8. Codeforces Round #408 (Div. 2) D - Police Stations

    地址:http://codeforces.com/contest/796/problem/D 题目: D. Police Stations time limit per test 2 seconds ...

  9. TOSCA自动测试工具跟QTP 和 Selenium的简单对比

    1. 一个课程里的,可以做个简单的参考,有些地方不是很准确

  10. IDEA 2018.2破解

    最新的IDEA激活方式 使用网上传统的那种输入网址的方式激活不了,使用http://idea.lanyus.com/这个网站提供的工具进行 1.进入hosts文件中:C:\Windows\System ...