费用流的简单应用

题目链接:luogu4013

 

题目:

给定一个由 n 行数字组成的数字梯形如下图所示。

 

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

分别遵守以下规则:

  1. 从梯形的顶至底的 m 条路径互不相交;

  2. 从梯形的顶至底的 m 条路径仅在数字结点处相交;

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

输入输出格式

输入格式:

第 1 行中有 2 个正整数 m 和 n ,分别表示数字梯形的第一行有 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

 本来以为会是一道dp题,没想到是网络流。

 

  大致思路就是用费用来代表每个点的收益,然后用容量来控制这个点和边可以走多少次,最后跑最大费用最大流就行了。

规则一:

既然每个点只能走一次,就把每个点拆成两个点(i,a),(i,b),这两个点之间连一条容量为1,费用为i权值的边。既然每条边也不能相交(也就是不能重合),所以就从i向其左下和右下的点(j,a)分别连一条容量为一,费用为0的边。最后就是常用的建源点的汇点,建一个源点,将其与顶部的每个点连一条容量为1,费用为0的边(因为顶部的点也只能跑一次)。在建一个汇点,将其与底部 每个点连一条容量为1,费用为0的边。

规则二:

参考规则一进行修改即可。但是这次点可以重复走,所以就不拆点。既然边都是不能相交(重合),那就还是将点i与下面每条j连一条容量为1,费用为i权值的边。因为这次不拆点,所以要想把权值加进去,就得把这条连边的费用改为权值。然后源点的连边不变,因为每个路径的起点肯定不同。因为每个点(除了起点)可以走无数次,所以底部的点与汇点的连边容量为无穷大。费用为底部点的权值

规则三:

参考规则二,这次边也可以重复,所以就将每条从i到j的连边的容量改为无穷大。费用还是i的权值。顶部和底部的连边还是不变。

最后每个规则跑一遍最大费用最大流,所得的费用就是答案。

代码:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<queue>
  4 #include<cstring>
  5 using namespace std;
  6 const int INF=9999999;
  7 const int N=100000;
  8 int a[100][100],b[100][100],vis[N];
  9 int n,m,head[N],ejs;
 10 struct node
 11 {
 12     int u,v,nxt,w,siz;
 13 }edg[N];
 14 void add(int u,int v,int w,int siz)
 15 {
 16     edg[++ejs].v=v;
 17     edg[ejs].u=u;
 18     edg[ejs].w=w;
 19     edg[ejs].siz=siz;
 20     edg[ejs].nxt=head[u];
 21     head[u]=ejs;
 22     edg[++ejs].u=v;
 23     edg[ejs].v=u;
 24     edg[ejs].siz=0;
 25     edg[ejs].w=-w;
 26     edg[ejs].nxt=head[v];
 27     head[v]=ejs;
 28 }
 29 int rank[100][100],dis[N],faz[N],js;
 30 queue<int>q;
 31 int change(int i)
 32 {
 33     return i%2?i+1:i-1;
 34 }
 35 void clear()
 36 {
 37     memset(head,0,sizeof(head));
 38     memset(edg,0,sizeof(edg));
 39     ejs=0;
 40 }
 41 bool spfa(int s,int t)
 42 {
 43     for(int i=1;i<=t;++i) dis[i]=-INF;
 44     memset(vis,0,sizeof(vis));
 45     memset(faz,0,sizeof(faz));
 46     while(!q.empty()) q.pop();
 47     q.push(s);
 48     dis[s]=0;
 49     vis[s]=1;
 50     while(!q.empty())
 51     {
 52         int u=q.front();
 53         q.pop();
 54         vis[u]=0;
 55         for(int i=head[u];i;i=edg[i].nxt)
 56         {
 57             int v=edg[i].v;
 58             if(dis[u]+edg[i].w>dis[v]&&edg[i].siz)
 59             {
 60                 faz[v]=i;
 61                 dis[v]=dis[u]+edg[i].w;
 62                 if(!vis[v])
 63                 {
 64                     q.push(v);
 65                     vis[v]=1;
 66                 }
 67                 
 68             }
 69         }
 70     }
 71     if(dis[t]!=-INF) return 1;
 72     return 0;
 73 }
 74 int work(int s,int t)
 75 {
 76     int ans=0;
 77     while(spfa(s,t))
 78     {
 79         int k=t,minn=0x7fffff;
 80         int now=t;
 81         while(now!=s)
 82         {
 83             minn=min(edg[faz[now]].siz,minn);
 84             now=edg[faz[now]].u;
 85         }
 86         now=t;
 87         while(now!=s)
 88         {
 89             edg[faz[now]].siz-=minn;
 90             edg[change(faz[now])].siz+=minn;
 91             now=edg[faz[now]].u;
 92         }
 93         ans+=dis[t]*minn;
 94     }
 95     return ans;
 96 }
 97 void rules1()
 98 {
 99     clear();
100     int s=0,t=js*2+1;
101     for(int i=1;i<=m;++i)
102     add(s,rank[1][i],0,1);
103     for(int i=1;i<=m+n-1;++i)
104     add(rank[n][i]+js,t,0,1);
105     for(int i=1;i<=n;++i)
106     for(int j=1;j<=i+m-1;++j)
107     {
108         
109         add(rank[i][j],rank[i][j]+js,a[i][j],1);
110         if(i==n) continue;
111         add(rank[i][j]+js,rank[i+1][j],0,1);
112         add(rank[i][j]+js,rank[i+1][j+1],0,1);
113     }
114     printf("%d\n",work(s,t));
115 }
116 void rules2()
117 {
118     clear();
119     int s=0,t=js+1;
120     for(int i=1;i<=m;++i) add(s,rank[1][i],0,1);
121     for(int i=1;i<m+n;++i) add(rank[n][i],t,a[n][i],INF);
122     for(int i=1;i<n;++i)
123     for(int j=1;j<=m+i-1;++j)
124     {
125         add(rank[i][j],rank[i+1][j],a[i][j],1);
126         add(rank[i][j],rank[i+1][j+1],a[i][j],1);
127     }
128     printf("%d\n",work(s,t));
129 }
130 
131 void rules3()
132 {
133     clear();
134     int s=0,t=js+1;
135     for(int i=1;i<=m;++i) add(s,rank[1][i],0,1);
136     for(int i=1;i<=n+m-1;++i) add(rank[n][i],t,a[n][i],INF);
137     for(int i=1;i<n;++i)
138     for(int j=1;j<=m+i-1;++j)
139     {
140         add(rank[i][j],rank[i+1][j],a[i][j],INF);
141         add(rank[i][j],rank[i+1][j+1],a[i][j],INF);
142     }
143     printf("%d\n",work(s,t));
144 }
145 int main()
146 {
147     scanf("%d%d",&m,&n);
148     for(int i=1;i<=n;++i)
149     {
150         for(int j=1;j<=m+i-1;++j)
151         {
152             scanf("%d",&a[i][j]);
153             rank[i][j]=++js;
154         }
155     }
156     rules1();
157     rules2();
158     rules3();
159     return 0;
160  } 
luogu4013

 

posted @ 2018-07-20 10:32  wxyww  阅读(151)  评论(0编辑  收藏  举报