数字梯形问题[网络流24题之16]

来源:互联网 发布:淘宝新品上架提醒 编辑:程序博客网 时间:2024/05/02 19:20

问题描述:

给定一个由 n 行数字组成的数字梯形如下图所示。梯形的第一行有 m 个数字。从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
规则 1:从梯形的顶至底的 m 条路径互不相交。
规则 2:从梯形的顶至底的 m 条路径仅在数字结点处相交。
规则 3:从梯形的顶至底的 m 条路径允许在数字结点相交或边相交。


编程任务:

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


数据输入:

1 行中有 2 个正整数 mnm,n<=20 ,分别表示数字梯形的第一行有 m 个数字,共有 n 行。接下来的 n 行是数字梯形中各行的数字。第 1 行有 m 个数字,第 2 行有 m+1 个数字


结果输出:

输出共三行,分别是按照规则 1,规则 2,规则 3 计算出的最大数字总和。


输入文件示例:

2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1


输出文件示例:

66
75
77


分析:

记第 (i,j) (i 表示行, j 表示列 ) 个数字为 ai,j
规则 1 因为这 m 条路径不能相交,所以可以考虑到拆点,即将第 (i,j) 个数字拆成 (i,j)0(i,j)1 ,再从 (i,j)0 引一条容量为 1 ,权值为 0 的有向边向 (i,j)1,这样就保证了,每一个数字只会经过一遍,以下是构建图 N 过程

1 :新增源 S 和汇 T
2 :在 S(1,j)0(1<=j<=m) 之间连一条容量为 1 ,权值为 ai,j 的有向边
3 :在 (i,j)0(1<=i<=n,1<=j<i+m)(i,j)1 之间连一条容量为 1 ,权值为 0 的有向边
4 :在 (i,j)1(1<=i<n,1<=j<i+m)(i+1,j)0 之间连一条容量为 1 ,权值为 ai+1,j 的有向边
5 :在 (i,j)1(1<=i<n,i<=j<i+m)(i+1,j+1)0 之间连一条容量为 1 ,权值为 ai+1,j+1 的有向边
6 :在 (n,j)1(1<=j<n+m)T 之间连一条容量为 + ,权值为 0 的有向边

图中的最小费用最大流的相反数就是规则 1 的答案

规则 2 :因为点可以经过任意次,所以只需要把(i,j)0(1<=i<=n,1<=j<i+m)(i,j)1 的边的容量修改为 + ,在求最小费用最大流即可,其相反数就是答案

规则 3 :因为边也可以随便走多少次了,所以只需要把除从 S 出发的边的容量修改为 + ,在求最小费用最大流即可,其相反数就是答案


代码:

#include <cstdio>#include <cstring>#include <algorithm>#include <queue>using namespace std; const int inf = 0x3f3f3f3f;int head[447],nxt[4747],to[4747],wei[4747],cost[4747],tot=1,tot1,tot2;queue<int >que;int dis[447],pre[447],pres[447];bool vis[447];int mark[47][47],a[47][47],re=0;int n,m;int used;void add(int,int,int,int);bool bfs();void dinic();int main(){    scanf("%d%d",&m,&n);    for(int i=1;i<=n;++i)        for(int j=1;j<=i+m-1;++j){            scanf("%d",&a[i][j]);            mark[i][j] = ++re;            add(mark[i][j]<<1,(mark[i][j]<<1)+1,1,0);        }    for(int i=1;i<n+m;++i)        add((mark[n][i]<<1)+1,446,1,0);    tot1 = tot;    for(int i=1;i<n;++i)        for(int j=1;j<m+i;++j){            add((mark[i][j]<<1)+1,mark[i+1][j]<<1,1,-a[i+1][j]);            add((mark[i][j]<<1)+1,mark[i+1][j+1]<<1,1,-a[i+1][j+1]);        }    tot2 = tot;    for(int i=1;i<=m;++i)        add(445,mark[1][i]<<1,1,-a[1][i]);    used = 0;    while(bfs())        dinic();    printf("%d",-used);    for(int i=2;i<tot;i+=2){        wei[i] += wei[i^1];        wei[i^1] = 0;    }    for(int i=2;i<tot1;++i)        wei[i] = inf;    used = 0;    while(bfs())        dinic();    printf("\n%d",-used);    for(int i=2;i<tot;i+=2){        wei[i] += wei[i^1];        wei[i^1] = 0;    }    for(int i=2;i<tot2;i+=2)        wei[i] = inf;    used = 0;    while(bfs())        dinic();    printf("\n%d",-used);    return 0;}void add(int from,int tp,int value,int spend){    ++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=value;cost[tot]=spend;    ++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0;cost[tot]=-spend;}bool bfs(){    memset(vis,false,sizeof vis);    memset(dis,0x3f,sizeof dis);    dis[445] = 0;    que.push(445);    int now;    do{        now = que.front();        vis[now] = false;        que.pop();        for(int i=head[now];i;i=nxt[i])            if(dis[to[i]]>dis[now]+cost[i] && wei[i]){                dis[to[i]] = dis[now]+cost[i];                pre[to[i]] = now;                pres[to[i]] = i;                if(!vis[to[i]]){                    vis[to[i]] = true;                    que.push(to[i]);                }            }    }while(!que.empty());    return dis[446]!=inf;}void dinic(){    int now = 446;    int low = inf;    while(now != 445){        low = min(low,wei[pres[now]]);        now = pre[now];    }    used += low*dis[446];    now = 446;    while(now != 445){        wei[pres[now]] -= low;        wei[pres[now]^1] += low;        now = pre[now];    }}
0 0
原创粉丝点击