题目链接:luogu4013
题目:
给定一个由 n 行数字组成的数字梯形如下图所示。
梯形的第一行有 m 个数字。从梯形的顶部的 m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
分别遵守以下规则:
-
从梯形的顶至底的 m 条路径互不相交;
-
从梯形的顶至底的 m 条路径仅在数字结点处相交;
-
从梯形的顶至底的 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 }