cogs 738. [网络流24题] 数字梯形

来源:互联网 发布:nginx宕机原因 编辑:程序博客网 时间:2024/05/17 12:03
  1. [网络流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 52 33 4 59 10 9 11 1 10 1 1 1 1 10 12 1 1 */
0 0