2191. 数字梯形问题

题目链接

2191. 数字梯形问题

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

梯形的第一行有 \(m\) 个数字。

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

规则 \(1\):从梯形的顶至底的 \(m\) 条路径互不相交。

规则 \(2\):从梯形的顶至底的 \(m\) 条路径仅在数字结点处相交。

规则 \(3\):从梯形的顶至底的 \(m\) 条路径允许在数字结点相交或边相交。

image

对于给定的数字梯形,分别按照规则 \(1\),规则 \(2\),和规则 \(3\) 计算出从梯形的顶至底的 \(m\) 条路径,使这 \(m\) 条路径经过的数字总和最大。

输入格式

\(1\) 行中有 \(2\) 个正整数 \(m\)\(n\),分别表示数字梯形的第一行有 \(m\) 个数字,共有 \(n\) 行。

接下来的 \(n\) 行是数字梯形中各行的数字。第 \(1\) 行有 \(m\) 个数字,第 \(2\) 行有 \(m+1\) 个数字,以此类推。

输出格式

将按照规则 \(1\),规则 \(2\),和规则 \(3\) 计算出的最大数字总和输出,每行输出一个最大总和。

数据范围

\(1 \le n,m \le 20\),
梯形中的数字范围 \([1,1000]\)

输入样例:

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

解题思路

费用流,最大权不相交路径

建图
对于限制一,先建立源点 \(s\) 和汇点 \(t\),由于点受到限制且费用没法表示,所以需要拆点,即拆分为入点和出点,每个点的入点向其出点连边,由于每个点只能用一次,所以容量为 \(1\),费用为点权,由于第一行人数已经固定为 \(1\),从 \(s\) 到所有第一行的点连边,容量为 \(1\),费用为 \(0\),最后一行的所有点的出点向 \(t\) 连边,容量为 \(1\),费用为 \(0\)
其他两个限制相应地放开容量即可
\(\color{red}{为什么?}\)
把可行流的轨迹当作人的行动轨迹,不难发现,此时对于限制一,如果某个点的入点和出点有流量的话,且只会经过一次,表示选择该点且计算其费用,由于第一行已经确定了,从 \(s\) 出发的边都会
满流,即最后达到最大流时求解最大费用即为不相交路径最大权,故可行流与实际问题是一一对应的,其他两个限制同理

  • 时间复杂度:\(O(k\times (n+m)\times f)\)

代码

// Problem: 数字梯形问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2193/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=590*2+5,M=(20+39+590+590*2+10)*2,inf=1e9;
int m,n,cost[40][40],S,T,id[40][40],cnt;
int h[N],f[M],w[M],ne[M],e[M],idx;
int incf[N],pre[N],d[N],q[N];
bool st[N];
void add(int a,int b,int c,int d)
{
    e[idx]=b,f[idx]=c,w[idx]=d,ne[idx]=h[a],h[a]=idx++;
    e[idx]=a,f[idx]=0,w[idx]=-d,ne[idx]=h[b],h[b]=idx++;
}
bool spfa()
{
    memset(d,-0x3f,sizeof d);
    memset(incf,0,sizeof incf);
    d[S]=0,incf[S]=inf,q[0]=S;
    int hh=0,tt=1;
    while(hh!=tt)
    {
        int x=q[hh++];
        if(hh==N)hh=0;
        st[x]=false;
        for(int i=h[x];~i;i=ne[i])
        {
            int y=e[i];
            if(d[y]<d[x]+w[i]&&f[i])
            {
                d[y]=d[x]+w[i];
                pre[y]=i;
                incf[y]=min(incf[x],f[i]);
                if(!st[y])
                {
                    q[tt++]=y;
                    if(tt==N)tt=0;
                    st[y]=true;
                }
            }
        }
    }
    return incf[T]>0;
}
int EK()
{
    int cost=0;
    while(spfa())
    {
        int t=incf[T];
        cost+=t*d[T];
        for(int i=T;i!=S;i=e[pre[i]^1])f[pre[i]]-=t,f[pre[i]^1]+=t;
    }
    return cost;
}
int main()
{
	
    scanf("%d%d",&m,&n);
    S=++cnt,T=++cnt;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m+i-1;j++)
    	{
    		scanf("%d",&cost[i][j]);
    		id[i][j]=++cnt;
    	}
    	
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m+i-1;j++)
    	{
    		add(id[i][j]<<1,id[i][j]<<1|1,1,cost[i][j]);
    		if(i==1)
    			add(S,id[i][j]<<1,1,0);
    		if(i<n)
    			add(id[i][j]<<1|1,id[i+1][j]<<1,1,0),
    			add(id[i][j]<<1|1,id[i+1][j+1]<<1,1,0);
    		if(i==n)
    			add(id[i][j]<<1|1,T,1,0);
    	}
    printf("%d\n",EK());
    
    memset(h,-1,sizeof h);
    idx=0;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m+i-1;j++)
    	{
    		add(id[i][j]<<1,id[i][j]<<1|1,inf,cost[i][j]);
    		if(i==1)
    			add(S,id[i][j]<<1,1,0);
    		if(i<n)
    			add(id[i][j]<<1|1,id[i+1][j]<<1,1,0),
    			add(id[i][j]<<1|1,id[i+1][j+1]<<1,1,0);
    		if(i==n)
    			add(id[i][j]<<1|1,T,inf,0);
    	}
    printf("%d\n",EK());
    
    memset(h,-1,sizeof h);
    idx=0;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=m+i-1;j++)
    	{
    		add(id[i][j]<<1,id[i][j]<<1|1,inf,cost[i][j]);
    		if(i==1)
    			add(S,id[i][j]<<1,1,0);
    		if(i<n)
    			add(id[i][j]<<1|1,id[i+1][j]<<1,inf,0),
    			add(id[i][j]<<1|1,id[i+1][j+1]<<1,inf,0);
    		if(i==n)
    			add(id[i][j]<<1|1,T,inf,0);
    	}
    printf("%d\n",EK());
    return 0;
}
posted @ 2022-12-02 10:29  zyy2001  阅读(63)  评论(0编辑  收藏  举报