【网络流24题】【洛谷P4013】数字梯形问题【费用流】
题目大意:
题目链接:https://www.luogu.org/problemnew/show/P4013
给定一个由行数字组成的数字梯形如下图所示。
梯形的第一行有个数字。从梯形的顶部的个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
分别遵守以下规则:
- 从梯形的顶至底的条路径互不相交
- 从梯形的顶至底的条路径仅在数字结点处相交
- 从梯形的顶至底的条路径允许在数字结点相交或边相交。
思路:
可总算背下来费用流的板子了。
对于边是否能重复走,我们只要控制边的流量就可以了。
对于点是否能重复走,可以考虑拆点。对于问题1,入点和出点之间流量为1。问题2,3中,流量为即可。
入点和出点的费用就是该点的权值。
注意为了保证最大流为,需要把汇点拆点,之间连一条流量为,费用为0的边。
代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=2010,Inf=1e9;
int n,m,S,T,T_,cost,maxn,tot,head[N],dis[N],map[50][50],pre[N];
bool vis[N];
struct edge
{
int next,to,from,flow,cost;
}e[N*4];
int C(int x,int y)
{
return (m+m+x-2)*(x-1)/2+y;
}
void add(int from,int to,int flow,int cost)
{
e[++tot].to=to;
e[tot].from=from;
e[tot].flow=flow;
e[tot].cost=cost;
e[tot].next=head[from];
head[from]=tot;
}
bool spfa()
{
memset(dis,0xcf,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(S);
dis[S]=0; vis[S]=1;
while (q.size())
{
int u=q.front(),v;
q.pop();
vis[u]=0;
for (rr int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (e[i].flow&&dis[v]<dis[u]+e[i].cost)
{
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
return dis[T]>0;
}
void addflow()
{
int minflow=Inf;
for (rr int x=T;x!=S;x=e[pre[x]].from)
minflow=min(minflow,e[pre[x]].flow);
for (rr int x=T;x!=S;x=e[pre[x]].from)
{
e[pre[x]].flow-=minflow;
e[pre[x]^1].flow+=minflow;
}
cost+=dis[T]*minflow;
}
int MCMF()
{
while (spfa())
addflow();
return cost;
}
void make(int val1,int val2)
{
tot=1;
memset(head,-1,sizeof(head));
for (rr int i=1;i<=n;i++)
for (rr int j=1;j<=m+i-1;j++)
{
add(C(i,j),C(i,j)+maxn,val1,map[i][j]);
add(C(i,j)+maxn,C(i,j),0,-map[i][j]);
if (i<n)
{
add(C(i,j)+maxn,C(i+1,j),val2,0);
add(C(i+1,j),C(i,j)+maxn,0,0);
add(C(i,j)+maxn,C(i+1,j+1),val2,0);
add(C(i+1,j+1),C(i,j)+maxn,0,0);
}
}
for (rr int i=1;i<=m;i++)
{
add(S,C(1,i),1,0);
add(C(1,i),S,0,0);
}
for (rr int i=1;i<=m+n-1;i++)
{
add(C(n,i)+maxn,T_,val1,0);
add(T_,C(n,i)+maxn,0,0);
}
add(T_,T,m,0);
add(T,T_,0,0);
cost=0;
}
int main()
{
scanf("%d%d",&m,&n);
for (rr int i=1;i<=n;i++)
for (rr int j=1;j<=m+i-1;j++)
scanf("%d",&map[i][j]);
S=2009; T_=2008; T=2007;
maxn=C(n,n+m-1);
make(1,1); printf("%d\n",MCMF());
make(m,1); printf("%d\n",MCMF());
make(m,m); printf("%d\n",MCMF());
return 0;
}