• 【网络流24题】【洛谷P4013】数字梯形问题【费用流】


    题目大意:

    题目链接:https://www.luogu.org/problemnew/show/P4013
    给定一个由nn行数字组成的数字梯形如下图所示。

    梯形的第一行有mm个数字。从梯形的顶部的mm个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至底的路径。
    在这里插入图片描述
    分别遵守以下规则:

    1. 从梯形的顶至底的mm条路径互不相交
    2. 从梯形的顶至底的mm条路径仅在数字结点处相交
    3. 从梯形的顶至底的mm条路径允许在数字结点相交或边相交。

    思路:

    可总算背下来费用流的板子了。

    对于边是否能重复走,我们只要控制边的流量就可以了。
    对于点是否能重复走,可以考虑拆点。对于问题1,入点和出点之间流量为1。问题2,3中,流量为mm即可。
    入点和出点的费用就是该点的权值。
    注意为了保证最大流为mm,需要把汇点TT拆点,之间连一条流量为mm,费用为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
    ",MCMF());
    	make(m,1); printf("%d
    ",MCMF());
    	make(m,m); printf("%d
    ",MCMF());
    	return 0;
    }
    
  • 相关阅读:
    JavaScript实现常见排序算法
    执行环境与作用域
    几种常见的三列布局,中间自适应,两边定宽
    常见的两列布局
    CodeAtlas For Sublime Text
    增加调用路径查找
    增加调用被调用个数隐喻
    sublime 插件
    分析大工程
    Jmeter 分布式测试
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998155.html
Copyright © 2020-2023  润新知