「解题报告」[网络流24题] 16.数字梯形问题 (最大费用最大流)
题意
有一个有数字组成的梯形,
该梯形有 \(n\) 行, 第一行有 \(m\) 列, 每一行都比前一行多一列.
有 \(m\) 条从梯形顶部到底部的路径, 分别以第一行的 \(m\) 个元素作为起点, 每次可以往左下或右下移动.
分别回答满足以下三个条件时, \(m\) 条路径上数字总和的最大值.
- \(m\) 条路径互不相交.
- \(m\) 条路径可以点相交, 不可以边相交.
- \(m\) 条路径既可以点相交, 也可以边相交.
思路
这一道题其实就是 DAG 不相交路径的各种变式.
第一问, 按照套路, 每个点只能出现一次, 那么就把每个点分为入点和出点, 入点和出点之间连一条容量为 \(1\), 代价为 \(0\) 的边, 跑最大费用最大流就行了.
第二问, 本来是不用建入点和出点的, 但我们为了避免重复建图, 可以直接把入点到出点的边容量改为 \(inf\), 其他的和原来一样. 需要注意的是, 不能修改了边权后直接在残量网络上跑最大流, 因为这时已经找不到增广路了.
第三问, 一开始本来是想 \(dp\) 的....但是给出题人点面子...那么就把除了从源点 \(S\) 练出来的边以外, 所有边的边权都改为 \(inf\). (当然, 反向边除外).
代码
#include<bits/stdc++.h>
using namespace std;
const int _=60+7;
const int __=1202+7;
const int ___=3760+7;
const int inf=0x3f3f3f3f;
int n,m,S,T,a[_][_],num[_][_],dis[__],ans,cnt;
int lst[__],nxt[___],to[___],c[___],w[___],bk[___],tot=1;
bool vis[__],mst[___],cha[___];
queue<int> q;
void init(){
cin>>m>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=m+i-1;j++){
scanf("%d",&a[i][j]);
num[i][j]=++cnt;
}
}
S=(m+m+n-1)*n+1; T=S+1;
}
void add(int x,int y,int cap,int wgt){
if(x==S) mst[tot+1]=1;
if(y==x+cnt||y==T) cha[tot+1]=1;
nxt[++tot]=lst[x]; to[tot]=y; c[tot]=cap; w[tot]=wgt; lst[x]=tot; bk[tot]=cap;
nxt[++tot]=lst[y]; to[tot]=x; c[tot]=0; w[tot]=-wgt; lst[y]=tot;
}
void link(){
for(int i=1;i<=m;i++)
add(S,num[1][i],1,a[1][i]);
for(int i=1;i<=m+n-1;i++)
add(num[n][i]+cnt,T,1,0);
for(int i=1;i<n;i++)
for(int j=1;j<=m+i-1;j++){
add(num[i][j]+cnt,num[i+1][j],1,a[i+1][j]);
add(num[i][j]+cnt,num[i+1][j+1],1,a[i+1][j+1]);
}
for(int i=1;i<=cnt;i++)
add(i,i+cnt,1,0);
}
bool SPFA(){
for(int i=1;i<=T;i++){ dis[i]=-inf; vis[i]=0; }
while(!q.empty()) q.pop();
dis[S]=0; q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=0;
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(!c[i]||dis[v]>=dis[u]+w[i]) continue;
dis[v]=dis[u]+w[i];
if(!vis[v]){
q.push(v);
vis[v]=1;
}
}
}
return dis[T]!=-inf;
}
int dfs(int u,int flow){
if(u==T) return flow;
int rest=flow; vis[u]=1;
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(vis[v]||!c[i]||dis[v]!=dis[u]+w[i]) continue;
int cst=dfs(v,min(rest,c[i]));
if(cst==0) dis[v]=-inf;
else{
c[i]-=cst; c[i^1]+=cst;
rest-=cst; ans+=cst*w[i];
}
}
return flow-rest;
}
void Dinic(){
ans=0;
int flow;
while(SPFA())
do{
flow=dfs(S,inf);
}while(flow);
}
void change(int ty){
if(ty==2){
for(int i=2;i<=tot;i+=2){
if(cha[i]) c[i]=inf;
else c[i]=bk[i];
c[i^1]=0;
}
}
else{
for(int i=2;i<=tot;i+=2){
if(!mst[i]) c[i]=inf;
else c[i]=bk[i];
c[i^1]=0;
}
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("x.in","r",stdin);
#endif
init();
link();
Dinic();
printf("%d\n",ans);
change(2);
Dinic();
printf("%d\n",ans);
change(3);
Dinic();
printf("%d\n",ans);
return 0;
}