Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)

Description

给定一个由n 行数字组成的数字梯形如下图所示。梯形的第一行有m 个数字。从梯形的顶部的m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。

规则1:从梯形的顶至底的m条路径互不相交。

规则2:从梯形的顶至底的m条路径仅在数字结点处相交。

规则3:从梯形的顶至底的m条路径允许在数字结点相交或边相交。

对于给定的数字梯形,分别按照规则1,规则2,和规则3 计算出从梯形的顶至底的m条路径,使这m条路径经过的数字总和最大。

Input

第1 行中有2个正整数m和n(m,n<=20),分别表示数字梯形的第一行有m个数字,共有n 行。

接下来的n 行是数字梯形中各行的数字。第1 行有m个数字,第2 行有m+1 个数字

Output

将按照规则1,规则2,和规则3 计算出的最大数字总和输出,每行一个最大总和。

Sample Input

2 5

2 3

3 4 5

9 10 9 1

1 1 10 1 1

1 1 10 12 1 1

Sample Output

66

75

77

Http

Libre:https://loj.ac/problem/6010

Source

网络流,最大费用最大流

解决思路

三种规则大体上是差不多的,我们分开来看。

对于规则一,因为要求不能有点走两次,所以我们把点拆成两个,两个之间连容量为1,花费为该点权值的边,而因为一个点可以走到下面的点和右下的点,所以也分别连容量为1但花费为0的边。同时,建立一个汇点一个源点,源点连到第一行的m个数,容量为1花费为0的边,汇点连到最后一行的n+m-1个点,同样也是容量为1花费为0的边。

然后跑一遍最大费用最大流即可。

对于规则二,由于可以允许点走多次,所以我们有两种处理方法,一是把上面规则一中的图里连接两个拆开的点的边的容量改为无穷大,另一种方法是直接不拆点。

对于规则三,由于边和点都可以走多次,所以可以把所有边(除从源点出发连向第一行的边)的容量均改为无穷大。

需要注意的是,不管怎么改,从源点出发的边容量一定为1,因为要保证只能走m次。

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<algorithm>
  6. using namespace std;
  7. const int maxN=800;
  8. const int maxM=maxN*maxN*2;
  9. const int inf=2147483647;
  10. class Edge
  11. {
  12. public:
  13. int u,v,cost,flow;
  14. };
  15. int n,m;
  16. int nodesum=0;
  17. int cnt=-1;
  18. int Arr[maxN][maxN];
  19. int Map[maxN][maxN];
  20. int Head[maxN];
  21. int Next[maxM];
  22. Edge E[maxM];
  23. int Dist[maxN];
  24. bool inqueue[maxN];
  25. int Path[maxN];
  26. int Flow[maxN];
  27. int Queue[maxM];
  28. void Add_Edge(int u,int v,int cost,int flow);
  29. bool spfa();
  30. int main()
  31. {
  32. scanf("%d%d",&m,&n);
  33. for (int i=1;i<=n;i++)
  34. for (int j=1;j<=m+i-1;j++)
  35. {
  36. scanf("%d",&Arr[i][j]);
  37. nodesum++;//nodesum用来统计总的数的数量
  38. Map[i][j]=nodesum;//Map用来标记第i行j个数对应的编号是多少,方便后面拆点,对于编号为i的点,我们将其拆成i和i+nodesum两个点。另建立源点0和汇点nodesum*2+1
  39. }
  40. //Q1第一问
  41. cnt=-1;
  42. memset(Head,-1,sizeof(Head));
  43. for (int i=1;i<=m;i++)
  44. Add_Edge(0,i,0,1);//连接源点与第一行
  45. for (int i=1;i<=n;i++)
  46. for (int j=1;j<=i+m-1;j++)
  47. {
  48. Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],1);//连接拆开的两点
  49. if (i!=n)//连接下面和右下,注意要判断不在最后一行才行
  50. {
  51. Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,1);
  52. Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,1);
  53. }
  54. }
  55. for (int i=1;i<=m+n-1;i++)//连接最后一行与汇点
  56. Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,1);
  57. int Ans=0;
  58. while (spfa())//spfa求最大费用最大流
  59. {
  60. int now=nodesum*2+1;
  61. int last=Path[now];
  62. int nowflow=Flow[nodesum*2+1];
  63. while(now!=0)
  64. {
  65. E[last].flow-=nowflow;
  66. E[last^1].flow+=nowflow;
  67. now=E[last].u;
  68. last=Path[now];
  69. }
  70. Ans+=Flow[nodesum*2+1]*Dist[nodesum*2+1];
  71. }
  72. cout<<Ans<<endl;
  73. //Q2第二问
  74. cnt=-1;
  75. memset(Head,-1,sizeof(Head));
  76. for (int i=1;i<=m;i++)//连接源点与第一行
  77. Add_Edge(0,i,0,1);
  78. for (int i=1;i<=n;i++)
  79. for (int j=1;j<=m+i-1;j++)
  80. {
  81. Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],inf);//连接拆点后的两点,注意此时流量为无穷大
  82. if (i!=n)
  83. {
  84. Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,1);//连接下面和右下
  85. Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,1);
  86. }
  87. }
  88. for (int i=1;i<=n+m-1;i++)//连接最后一行与汇点,注意这里也是容量无穷大,因为有的路径可以在同一点结束
  89. Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,inf);
  90. Ans=0;
  91. while (spfa())//求解最大费用最大流
  92. {
  93. int now=nodesum*2+1;
  94. int last=Path[now];
  95. int nowflow=Flow[nodesum*2+1];
  96. while (now!=0)
  97. {
  98. E[last].flow-=nowflow;
  99. E[last^1].flow+=nowflow;
  100. now=E[last].u;
  101. last=Path[now];
  102. }
  103. Ans+=nowflow*Dist[nodesum*2+1];
  104. }
  105. cout<<Ans<<endl;
  106. //Q3第三问
  107. memset(Head,-1,sizeof(Head));
  108. cnt=-1;
  109. for (int i=1;i<=m;i++)
  110. Add_Edge(0,i,0,1);//连接源点与第一行,注意这里的容量还是为1
  111. for (int i=1;i<=n;i++)
  112. for (int j=1;j<=m+i-1;j++)
  113. {
  114. Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],inf);//同上面的连边,但容量均为无穷大
  115. if (i!=n)
  116. {
  117. Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,inf);
  118. Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,inf);
  119. }
  120. }
  121. for (int i=1;i<=n+m-1;i++)
  122. Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,inf);
  123. Ans=0;//求解最大费用最大流
  124. while (spfa())
  125. {
  126. int now=nodesum*2+1;
  127. int last=Path[now];
  128. int nowflow=Flow[nodesum*2+1];
  129. while (now!=0)
  130. {
  131. E[last].flow-=nowflow;
  132. E[last^1].flow+=nowflow;
  133. now=E[last].u;
  134. last=Path[now];
  135. }
  136. Ans+=nowflow*Dist[nodesum*2+1];
  137. }
  138. cout<<Ans<<endl;
  139. return 0;
  140. }
  141. void Add_Edge(int u,int v,int cost,int flow)
  142. {
  143. cnt++;
  144. Next[cnt]=Head[u];
  145. Head[u]=cnt;
  146. E[cnt].u=u;
  147. E[cnt].v=v;
  148. E[cnt].cost=cost;
  149. E[cnt].flow=flow;
  150. cnt++;//加反边
  151. Next[cnt]=Head[v];
  152. Head[v]=cnt;
  153. E[cnt].u=v;
  154. E[cnt].v=u;
  155. E[cnt].cost=-cost;
  156. E[cnt].flow=0;
  157. }
  158. bool spfa()
  159. {
  160. for (int i=0;i<=2*nodesum+1;i++)
  161. Dist[i]=-inf;
  162. memset(inqueue,0,sizeof(inqueue));
  163. int h=1,t=0;
  164. Queue[1]=0;
  165. inqueue[0]=1;
  166. Dist[0]=0;
  167. Flow[0]=inf;
  168. do
  169. {
  170. t++;
  171. int u=Queue[t];
  172. //cout<<Dist[u]<<endl;
  173. for (int i=Head[u];i!=-1;i=Next[i])
  174. {
  175. int v=E[i].v;
  176. //cout<<u<<"->"<<v<<endl;
  177. //cout<<Dist[u]+E[i].cost<<" "<<Dist[v]<<endl;
  178. //system("pause");
  179. if ((E[i].flow>0)&&(Dist[u]+E[i].cost>Dist[v]))
  180. {
  181. //cout<<"Modefy!"<<endl;
  182. Dist[v]=E[i].cost+Dist[u];
  183. Path[v]=i;
  184. Flow[v]=min(Flow[u],E[i].flow);
  185. if (inqueue[v]==0)
  186. {
  187. h++;
  188. Queue[h]=v;
  189. inqueue[v]=1;
  190. }
  191. }
  192. }
  193. inqueue[u]=0;
  194. }
  195. while(t<h);
  196. if (Dist[nodesum*2+1]==-inf)
  197. return 0;
  198. return 1;
  199. }

Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)的更多相关文章

  1. LOJ #6010. 「网络流 24 题」数字梯形

    #6010. 「网络流 24 题」数字梯形   题目描述 给定一个由 n nn 行数字组成的数字梯形如下图所示.梯形的第一行有 m mm 个数字.从梯形的顶部的 m mm 个数字开始,在每个数字处可以 ...

  2. 【刷题】LOJ 6010 「网络流 24 题」数字梯形

    题目描述 给定一个由 \(n\) 行数字组成的数字梯形如下图所示.梯形的第一行有 \(m\) 个数字.从梯形的顶部的 \(m\) 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至 ...

  3. 2018.10.15 loj#6010. 「网络流 24 题」数字梯形(费用流)

    传送门 费用流经典题. 按照题目要求建边. 为了方便我将所有格子拆点,三种情况下容量分别为111,infinfinf,infinfinf,费用都为validi,jval_{id_{i,j}}valid ...

  4. 【PowerOJ1751&网络流24题】数字梯形问题(费用流)

    题意: 思路: [问题分析] 求图的最大权不相交路径及其变种,用费用最大流解决. [建模方法] 规则(1) 把梯形中每个位置抽象为两个点<i.a>,<i.b>,建立附加源S汇T ...

  5. Libre 6012 「网络流 24 题」分配问题 (网络流,费用流)

    Libre 6012 「网络流 24 题」分配问题 (网络流,费用流) Description 有n件工作要分配给n个人做.第i个人做第j件工作产生的效益为\(c_{ij}\).试设计一个将n件工作分 ...

  6. Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流)

    Libre 6011 「网络流 24 题」运输问题 (网络流,最小费用最大流) Description W 公司有m个仓库和n个零售商店.第i个仓库有\(a_i\)个单位的货物:第j个零售商店需要\( ...

  7. LibreOJ 6004. 「网络流 24 题」圆桌聚餐 网络流版子题

    #6004. 「网络流 24 题」圆桌聚餐 内存限制:256 MiB时间限制:5000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数 ...

  8. liberOJ#6006. 「网络流 24 题」试题库 网络流, 输出方案

    #6006. 「网络流 24 题」试题库     题目描述 假设一个试题库中有 n nn 道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性.现要从题库中抽取 m mm 道题组成试卷.并要求 ...

  9. LibreOJ #6000. 「网络流 24 题」搭配飞行员

    二次联通门 : LibreOJ #6000. 「网络流 24 题」搭配飞行员 /* LibreOJ #6000. 「网络流 24 题」搭配飞行员 二分图最大匹配 Dinic最大流 + 当前弧优化 */ ...

随机推荐

  1. VB 批量重命名文件

    VERSION 5.00 Begin VB.Form Form1 BorderStyle = 3 'Fixed Dialog Caption = "Rename use VB QQ 1009 ...

  2. 20155207 EXP8 Web基础

    20155207 EXP8 Web基础 实验内容 (1)Web前端HTML (2)Web前端javascipt (3)Web后端:MySQL基础:正常安装.启动MySQL,建库.创建用户.修改密码.建 ...

  3. 20155333 《网络对抗》Exp2 后门原理与实践

    20155333 <网络对抗>Exp2 后门原理与实践 1.例举你能想到的一个后门进入到你系统中的可能方式? 下载的软件中捆绑有后门: 浏览的网页或其上的小广告: 有些网页会自动安装软件. ...

  4. Android开发——官方推荐使用DialogFragment替换AlertDialog

    )比如当屏幕旋转时,AlertDialog会消失,更不会保存如EditText上的文字,如果处理不当很可能引发异常,因为Activity销毁前不允许对话框未关闭.而DialogFragment对话框会 ...

  5. RegExp,实现匹配合法时间(24小时制)的正则表达式

    合法时间格式  00:00:00 - 23:59:59   格式分析:H + ":" + M + ":" + S   H-分析: 00:00:00 - 09:5 ...

  6. idea java方法中 传多个参数对象 的复制粘贴快速处理方法

    比如像这种的传多个参数对象,我是直接复制过来,然后把第一个字母改成大写,然后后面的实例对象敲一个第一个字符的小写,回车就直接出来了 在写调用参数的地方,ctrl+p 调出提示,然后按下提示里的实例的第 ...

  7. arduino新入手体验:三个小实验

    新入手体验:三个小实验 一:一个LED闪烁 控制要求:1个LED灯,每隔50ms闪烁一次 实物连接图: 控制代码: //2018.6/11 ;//定义数字接口10,对应 void setup() { ...

  8. 冲刺Two之站立会议1

    今天我们开始了第二个冲刺期的工作,大家重新讨论了下个阶段的工作内容,由于上次演示我们主要只是实现了摄像头开启.通信和语音通话的功能,而且各部分还有待完善.所以我们决定了之后的主要工作的内容:之前服务器 ...

  9. spring-boot随笔

    配置了spring-boot-starter-web的依赖后,会自动添加tomcat和spring mvc的依赖,那么spring boot 会对tomcat和spring mvc进行自动配置 < ...

  10. IOS的开发演变历史

    对IOS开发平台一直抱有很大兴趣,正好通过这个机会好好了解一下IOS的开发历程! 通过一些查阅,我了解到IOS的开发平台主要是依靠Xcode软件来编写程序,同时又需要在MAC OS X的环境下运行,w ...