【網絡流24題-洛谷-P4013】數字梯形問題(最大權不相交路徑、最小費用最大流)

題目描述

給定一個由 nn 行數字組成的數字梯形如下圖所示。

梯形的第一行有 mm 個數字。從梯形的頂部的 mm 個數字開始,在每個數字處可以沿左下或右下方向移動,形成一條從梯形的頂至底的路徑。

分別遵守以下規則:

  1. 從梯形的頂至底的 mm 條路徑互不相交;

  2. 從梯形的頂至底的 mm 條路徑僅在數字結點處相交;

  3. 從梯形的頂至底的 mm 條路徑允許在數字結點相交或邊相交。

輸入格式

第 11 行中有 22 個正整數 mm 和 nn,分別表示數字梯形的第一行有 mm 個數字,共有 nn 行。接下來的 nn行是數字梯形中各行的數字。

第 11 行有 mm 個數字,第 22 行有 m+1m+1 個數字,以此類推。

輸出格式

將按照規則 11,規則 22,和規則 33 計算出的最大數字總和並輸出,每行一個最大總和。

輸入輸出樣例

輸入 #1複製

2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1

輸出 #1複製

66
75
77

說明/提示

1≤m,n≤20

思路:

點不能重合,那麼拆點,將其流量設置爲1,費用爲點權。

點可以重合,但邊不能重合,不用拆點,點權放在這個點的出邊上,除了最後一排點的出邊的流量爲inf,其餘的邊的流量都爲1。因爲最後一排點到匯點的邊是代表選擇最後一個點,點能重合所以這裏流量爲inf。

最後邊能重合,則將中間的邊的流量設置爲inf即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<vector>
#include<unordered_map>
#define mod (1000000007)
using namespace std;
typedef long long ll;
const int MAX = 2e5 + 5;
const int inf = 0x3f3f3f3f;
struct node {
    int v,f,w,nxt;
} e[50005<<2];
int n,m,N;
int head[MAX],d[MAX],vis[MAX],tot=1,p[MAX];
void add(int u,int v,int f,int cost=0) {
    e[++tot].v = v;e[tot].f = f;e[tot].w = cost;e[tot].nxt = head[u];head[u] = tot;
    e[++tot].v = u;e[tot].f = 0; e[tot].w = -cost;e[tot].nxt = head[v];head[v] = tot;
}
bool bfs(int s,int t) {
    for(int i = 0; i<=N; i++) 
	d[i]=inf,vis[i]=0;
    d[s]=0;
    queue<int>q;
    q.push(s);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u]; ~i; i=e[i].nxt) {
            int j=e[i].v;
            if(e[i].f&&d[j]>d[u]+e[i].w) {
                d[j]=d[u]+e[i].w;
                p[j]=i;
                if(!vis[j])vis[j]=1,q.push(j);
            }
        }
    }
    return d[t]<inf;
}
int MCMF(int s,int t,int &flow) {
    int ans=0;
    while(bfs(s,t)) {
        int x=t,f=inf;
        while(x!=s) {
            f = min(f,e[p[x]].f),x=e[p[x]^1].v;
        }
        flow += f;
        ans+=1LL*d[t]*f;
        x=t;
        while(x!=s) {
            e[p[x]].f-=f,e[p[x]^1].f+=f;
            x=e[p[x]^1].v;
        }
    }
    return ans;
}
int a[50][50],id[50][50],ii=0;
int main() {
    scanf("%d%d",&m,&n);
    int st=0,ed,fl=0;N=ed;
    
    for(int i=0;i<n;i++){
    	for(int j=1;j<=m+i;j++){
    		scanf("%d",&a[i+1][j]);
			id[i+1][j]=++ii;
		}
	}
	ed=2*ii+1;N=ed;//N記得賦初值 
	for(int i=0;i<=ed+1;i++) head[i]=-1;//上限設置小了 
	for(int i=1;i<=m;i++) add(st,id[1][i],1,0);
	for(int i=1;i<=n;i++) for(int j=1;j<m+i;j++) add(id[i][j],id[i][j]+ii,1,-a[i][j]);
	for(int i=1;i<n;i++) for(int j=1;j<m+i;j++) add(id[i][j]+ii,id[i+1][j],1,0),add(id[i][j]+ii,id[i+1][j+1],1,0);
	for(int i=1;i<m+n;i++) add(id[n][i]+ii,ed,1,0);
	int ans=-MCMF(st,ed,fl);
	
	tot=1;
	for(int i=0;i<=ed+1;i++) head[i]=-1;
	for(int i=1;i<=m;i++) add(st,id[1][i],1,0);//只有m條路徑,所以這裏必須是1,不能是inf。 
	//因爲邊不重合,所以下面每條邊只能走一次,但一個點的不同路徑走的話都能獲得這個點的值 
	for(int i=1;i<n;i++) for(int j=1;j<m+i;j++) add(id[i][j],id[i+1][j],1,-a[i][j]),add(id[i][j],id[i+1][j+1],1,-a[i][j]); 
	for(int i=1;i<m+n;i++) add(id[n][i],ed,inf,-a[n][i]);//這裏爲inf因爲最後一個點可能被選多次,這裏不存在邊相互交叉的問題 
	int ans2=-MCMF(st,ed,fl);

	tot=1;
	for(int i=0;i<=ed+1;i++) head[i]=-1;
	for(int i=1;i<=m;i++) add(st,id[1][i],1,0);
	//邊也可以走多次,則下面邊的流量也設置爲inf 
	for(int i=1;i<n;i++) for(int j=1;j<m+i;j++) add(id[i][j],id[i+1][j],inf,-a[i][j]),add(id[i][j],id[i+1][j+1],inf,-a[i][j]); 
	for(int i=1;i<m+n;i++) add(id[n][i],ed,inf,-a[n][i]);
	int ans3=-MCMF(st,ed,fl);
	printf("%d\n%d\n%d\n",ans,ans2,ans3);
	
	return 0 ;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章