- [网络流24题] 数字梯形
★★★ 输入文件:digit.in 输出文件:digit.out 简单对比
时间限制:1 s 内存限制:128 MB
«问题描述:
给定一个由n 行数字组成的数字梯形如下图所示。梯形的第一行有m 个数字。从梯形
的顶部的m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶
至底的路径。
规则1:从梯形的顶至底的m条路径互不相交。
规则2:从梯形的顶至底的m条路径仅在数字结点处相交。
规则3:从梯形的顶至底的m条路径允许在数字结点相交或边相交。
«编程任务:
对于给定的数字梯形,分别按照规则1,规则2,和规则3 计算出从梯形的顶至底的m
条路径,使这m条路径经过的数字总和最大。
«数据输入:
由文件digit.in提供输入数据。文件的第1 行中有2个正整数m和n(m,n<=20),分别
表示数字梯形的第一行有m个数字,共有n 行。接下来的n 行是数字梯形中各行的数字。
第1 行有m个数字,第2 行有m+1 个数字,…。
«结果输出:
程序运行结束时,将按照规则1,规则2,和规则3 计算出的最大数字总和输出到文件
digit.out中。每行一个最大总和。
输入文件示例 输出文件示例
digit.in
2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1
digit.out
66
75
77
【分析】
一道略微有点复杂的最大费用最大流…不过仔细一点还是很好想的
解法参见注释
【代码】
//cogs 738. [网络流24题] 数字梯形
//1.拆点限制容量
//2.所有边(除了汇点直连边)容量为1
//3.除了源点直连边容量为1,其它边容量为m
//最大费用最大流
#include<iostream>
#include<climits>
#include<cstdio>
#include<queue>
#include<cstring>
#define inf 1e9+7
#define p(i,j) (i-1)*40+j
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=10005;
queue <int> q;
bool vis[mxn];
int n,m,s,t,ans,cnt;
int head[mxn],dis[mxn],pre[mxn],c[25][25];
struct node {int from,to,d,next,flow;} f[mxn<<2];
inline void add(int u,int v,int flow,int d)
{
f[++cnt].to=v,f[cnt].from=u,f[cnt].next=head[u],f[cnt].flow=flow,f[cnt].d=d,head[u]=cnt;
f[++cnt].to=u,f[cnt].from=v,f[cnt].next=head[v],f[cnt].flow=0,f[cnt].d=-d,head[v]=cnt;
}
inline bool spfa()
{
int i,j,d,u,v,flow,ttt;
memset(dis,-0x3f,sizeof dis);ttt=dis[0];
memset(vis,0,sizeof vis);
dis[s]=0;
q.push(s);
while(!q.empty())
{
u=q.front();
q.pop();
vis[u]=0;
for(i=head[u];i;i=f[i].next)
{
v=f[i].to,flow=f[i].flow,d=f[i].d;
if(dis[v]<dis[u]+d && flow>0)
{
dis[v]=dis[u]+d;
pre[v]=i; //记录前驱边
if(!vis[v])
q.push(v),vis[v]=1;
}
}
}
return dis[t]>ttt;
}
inline void maxflow()
{
int i,j,u,v,d,flow,tmp=inf;
for(i=pre[t];i;i=pre[f[i].from])
tmp=min(tmp,f[i].flow);
ans+=dis[t]*tmp;
for(i=pre[t];i;i=pre[f[i].from])
{
f[i].flow-=tmp;
if(i&1) f[i+1].flow+=tmp;
else f[i-1].flow+=tmp;
}
}
int main()
{
freopen("digit.in","r",stdin);
freopen("digit.out","w",stdout);
int i,j,k,u,v,w,d;
scanf("%d%d",&m,&n);
fo(i,1,n)
fo(j,1,i+m-1)
scanf("%d",&c[i][j]);
s=0,t=2000;
//1.不能相交 ——拆点
fo(j,1,m) add(s,p(1,j),1,c[1][j]); //add(p(1,i),p(1,i)+800,1,0);
fo(i,1,n-1)
fo(j,1,i+m-1)
{
add(p(i,j),p(i,j)+800,1,0);
add(p(i,j)+800,p(i+1,j),1,c[i+1][j]);
add(p(i,j)+800,p(i+1,j+1),1,c[i+1][j+1]);
}
fo(j,1,n+m-1) add(p(n,j),p(n,j)+800,1,0),add(p(n,j)+800,t,1,0);
while(spfa()) maxflow();
printf("%d\n",ans);
//2.仅有点相交,除了与汇点直连的点以外各边容量为1
M(head);M(pre);ans=cnt=0;
fo(j,1,m) add(s,p(1,j),1,c[1][j]);
fo(i,1,n-1)
fo(j,1,i+m-1)
{
add(p(i,j),p(i+1,j),1,c[i+1][j]);
add(p(i,j),p(i+1,j+1),1,c[i+1][j+1]);
}
fo(j,1,n+m-1) add(p(n,j),t,m,0);
while(spfa()) maxflow();
printf("%d\n",ans);
//3.随便跑,容量m(inf)
M(head);M(pre);ans=cnt=0;
fo(j,1,m) add(s,p(1,j),1,c[1][j]);
fo(i,1,n-1)
fo(j,1,i+m-1)
{
add(p(i,j),p(i+1,j),m,c[i+1][j]);
add(p(i,j),p(i+1,j+1),m,c[i+1][j+1]);
}
fo(j,1,n+m-1) add(p(n,j),t,m,0);
while(spfa()) maxflow();
printf("%d\n",ans);
return 0;
}
/*
2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1
*/