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次。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std; const int maxN=800;
const int maxM=maxN*maxN*2;
const int inf=2147483647; class Edge
{
public:
int u,v,cost,flow;
}; int n,m;
int nodesum=0;
int cnt=-1;
int Arr[maxN][maxN];
int Map[maxN][maxN];
int Head[maxN];
int Next[maxM];
Edge E[maxM];
int Dist[maxN];
bool inqueue[maxN];
int Path[maxN];
int Flow[maxN];
int Queue[maxM]; void Add_Edge(int u,int v,int cost,int flow);
bool spfa(); int main()
{
scanf("%d%d",&m,&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=m+i-1;j++)
{
scanf("%d",&Arr[i][j]);
nodesum++;//nodesum用来统计总的数的数量
Map[i][j]=nodesum;//Map用来标记第i行j个数对应的编号是多少,方便后面拆点,对于编号为i的点,我们将其拆成i和i+nodesum两个点。另建立源点0和汇点nodesum*2+1
}
//Q1第一问
cnt=-1;
memset(Head,-1,sizeof(Head));
for (int i=1;i<=m;i++)
Add_Edge(0,i,0,1);//连接源点与第一行
for (int i=1;i<=n;i++)
for (int j=1;j<=i+m-1;j++)
{
Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],1);//连接拆开的两点
if (i!=n)//连接下面和右下,注意要判断不在最后一行才行
{
Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,1);
Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,1);
}
}
for (int i=1;i<=m+n-1;i++)//连接最后一行与汇点
Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,1);
int Ans=0;
while (spfa())//spfa求最大费用最大流
{
int now=nodesum*2+1;
int last=Path[now];
int nowflow=Flow[nodesum*2+1];
while(now!=0)
{
E[last].flow-=nowflow;
E[last^1].flow+=nowflow;
now=E[last].u;
last=Path[now];
}
Ans+=Flow[nodesum*2+1]*Dist[nodesum*2+1];
}
cout<<Ans<<endl;
//Q2第二问
cnt=-1;
memset(Head,-1,sizeof(Head));
for (int i=1;i<=m;i++)//连接源点与第一行
Add_Edge(0,i,0,1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m+i-1;j++)
{
Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],inf);//连接拆点后的两点,注意此时流量为无穷大
if (i!=n)
{
Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,1);//连接下面和右下
Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,1);
}
}
for (int i=1;i<=n+m-1;i++)//连接最后一行与汇点,注意这里也是容量无穷大,因为有的路径可以在同一点结束
Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,inf);
Ans=0;
while (spfa())//求解最大费用最大流
{
int now=nodesum*2+1;
int last=Path[now];
int nowflow=Flow[nodesum*2+1];
while (now!=0)
{
E[last].flow-=nowflow;
E[last^1].flow+=nowflow;
now=E[last].u;
last=Path[now];
}
Ans+=nowflow*Dist[nodesum*2+1];
}
cout<<Ans<<endl;
//Q3第三问
memset(Head,-1,sizeof(Head));
cnt=-1;
for (int i=1;i<=m;i++)
Add_Edge(0,i,0,1);//连接源点与第一行,注意这里的容量还是为1
for (int i=1;i<=n;i++)
for (int j=1;j<=m+i-1;j++)
{
Add_Edge(Map[i][j],Map[i][j]+nodesum,Arr[i][j],inf);//同上面的连边,但容量均为无穷大
if (i!=n)
{
Add_Edge(Map[i][j]+nodesum,Map[i+1][j],0,inf);
Add_Edge(Map[i][j]+nodesum,Map[i+1][j+1],0,inf);
}
}
for (int i=1;i<=n+m-1;i++)
Add_Edge(Map[n][i]+nodesum,nodesum*2+1,0,inf);
Ans=0;//求解最大费用最大流
while (spfa())
{
int now=nodesum*2+1;
int last=Path[now];
int nowflow=Flow[nodesum*2+1];
while (now!=0)
{
E[last].flow-=nowflow;
E[last^1].flow+=nowflow;
now=E[last].u;
last=Path[now];
}
Ans+=nowflow*Dist[nodesum*2+1];
}
cout<<Ans<<endl;
return 0;
} void Add_Edge(int u,int v,int cost,int flow)
{
cnt++;
Next[cnt]=Head[u];
Head[u]=cnt;
E[cnt].u=u;
E[cnt].v=v;
E[cnt].cost=cost;
E[cnt].flow=flow; cnt++;//加反边
Next[cnt]=Head[v];
Head[v]=cnt;
E[cnt].u=v;
E[cnt].v=u;
E[cnt].cost=-cost;
E[cnt].flow=0;
} bool spfa()
{
for (int i=0;i<=2*nodesum+1;i++)
Dist[i]=-inf;
memset(inqueue,0,sizeof(inqueue));
int h=1,t=0;
Queue[1]=0;
inqueue[0]=1;
Dist[0]=0;
Flow[0]=inf;
do
{
t++;
int u=Queue[t];
//cout<<Dist[u]<<endl;
for (int i=Head[u];i!=-1;i=Next[i])
{
int v=E[i].v;
//cout<<u<<"->"<<v<<endl;
//cout<<Dist[u]+E[i].cost<<" "<<Dist[v]<<endl;
//system("pause");
if ((E[i].flow>0)&&(Dist[u]+E[i].cost>Dist[v]))
{
//cout<<"Modefy!"<<endl;
Dist[v]=E[i].cost+Dist[u];
Path[v]=i;
Flow[v]=min(Flow[u],E[i].flow);
if (inqueue[v]==0)
{
h++;
Queue[h]=v;
inqueue[v]=1;
}
}
}
inqueue[u]=0;
}
while(t<h);
if (Dist[nodesum*2+1]==-inf)
return 0;
return 1;
}

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. FakeID签名漏洞分析及利用(一)

    作者:申迪   转载请注明出处: http://blogs.360.cn/360mobile BlueBox于7月30日宣布安卓从2010年以来一直存在一个apk签名问题[1],并且会在今年Black ...

  2. 20155210 Exp9 Web安全基础实践

    Exp9 Web安全基础实践 实验过程 开启webgoat 输入java -jar webgoat-container-7.1-exec.jar,来运行webgoat 在浏览器输入localhost: ...

  3. 20155318 Exp1 PC平台逆向破解(5)M

    20155318 Exp1 PC平台逆向破解(5)M 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入 ...

  4. 20155339 Exp3 免杀原理与实践

    20155339 Exp3 免杀原理与实践 基础问题 (1)杀软是如何检测出恶意代码的? 基于特征码的检测(杀软的特征库中包含了一些数据或者数据段,杀软会尽可能的更新这个特征库,以包括尽可能多的恶意代 ...

  5. 实践:IIS7下访问ashx页面,显示404

    问题描述 1.路径什么的都对,这方面的原因就不要想了 2.在我的电脑上可以,在同事的电脑上不可以 方案1:未注册ashx的处理应用程序 也就是不知道IIS不知道用什么应用程序处理ashx文件,解决办法 ...

  6. Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法

    Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法 先上个图.语法提示支持到 Microsoft Dynamics xRM API 8.2 也就是cr ...

  7. Win环境 Android Studio使用Git 教程 ( 生成SSH key )

    Github和码云都提供SSH协议,即用户可以用公钥认证方式连接到码云的SSH服务器.这就需要生成并部署SSH Key.下面就是我生成SSH Key的步骤,希望有所帮助: Git生成SSH key 在 ...

  8. linuxC/C++面试问题总结整理

    linuxC/C++面试问题总结整理 因为一些原因重新找工作了,面的linux c/c++,这里把面试中经常碰到的问题总结一下. linuxC/C++面试问题总结整理 单元测试 关键字const 关键 ...

  9. 原生js实现table的排序

    原生js实现table的排序 今天遇到了一个问题就是使用原生js对table标签进行排序 一开始的时候陷入了一个误区就是首先获取table,然后每次比较完大小都会交换children的值,准备到最后吧 ...

  10. B1048 数字加密

    15/20 #include<bits/stdc++.h> using namespace std; stack<int> s; char a[3]={'J','Q','K'} ...