P4013 数字梯形问题(拆点+最大费最大流)

传送门

题目描述:

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

P4013 数字梯形问题(拆点+最大费最大流)

P4013 数字梯形问题(拆点+最大费最大流)

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

分别遵守以下规则:

  1. 从梯形的顶至底的 m 条路径互不相交;

  2. 从梯形的顶至底的 m条路径仅在数字结点处相交;

         3.从梯形的顶至底的 m条路径允许在数字结点相交或边相交。

思路:最大费用最大流,拆点,一个问建一次图,

第一问:要路径不相交,意思就是m条路径的边和节点都不相交,则拆点后两点连容量为1,价值为自身数字num的边,

限制为只选择一次,然后上面点的出点和下边两个点的入点连边,容量也设置为1.边没有价值,设为0,然后s对m个起点

连容量为1,价值为0的边,表示选取m条路径,再把最下层的所有点的出点与汇点连边,容量为1,费用为0;

第二问:数字可以重复选择,路径还是不能重复,那么就把拆点后的两点容量设置为inf,表示可以无线选取,边的容量不变,与汇点

连边容量也设置为inf,因为从一个点流出的流量可能比1大(最后一个点是多条路径的终点)

第三问:边和路径都可以重复选择,就在上题基础上把边的容量也设置为inf就好了,总之源点与m个起始点的容量为1的边不变就行.

第三问也可以用dp做,时间复杂度O(n*m)速度更快

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 600005;
const int inf = 0x3f3f3f3f;
struct edge {
    int f, t, nxt;
    ll flow, w;
}e[maxn * 2];
int hd[maxn], tot = 1;
void add(int f, int t, ll flow, ll w) {
    e[++tot] = { f,t,hd[f],flow ,w };
    hd[f] = tot;
    e[++tot] = { t,f,hd[t],0,-w };
    hd[t] = tot;
}
int s, t;
int dis[maxn], pre[maxn];
ll maxflow, mincost;
ll cyf[maxn];
bool inque[maxn];
int m, n, total;
bool spfa() {
    for (int i = 1; i <=total*2+3; i++) {
        dis[i] = -inf; inque[i] = 0;
    }
    dis[s] = 0, inque[s] = 1, cyf[s] = inf;
    queue<int>q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        inque[u] = 0;
        for (int i = hd[u]; i; i = e[i].nxt) {
            int v = e[i].t;
            if (e[i].flow > 0 && dis[u] + e[i].w > dis[v]) {
                pre[v] = i;
                cyf[v] = min(cyf[u], e[i].flow);
                dis[v] = dis[u] + e[i].w;
                if (!inque[v]) {
                    inque[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return dis[t] != -inf;
}
void mcmf() {
    maxflow = mincost = 0;
    while (spfa()) {
        int x = t; maxflow += cyf[t]; mincost += dis[t] * cyf[t];
        while (x != s) {
            int i = pre[x];
            e[i].flow -= cyf[t];
            e[i ^ 1].flow += cyf[t];
            x = e[i].f;
        }
    }
}
int num[25][3000];
int res[25][3000];
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d%d", &m, &n);
    total = (2 * m + n - 1) * n / 2;
    s = total*2 + 1, t = s + 1;
    int nxt , pre;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m + i - 1; j++) {
            scanf("%d", &num[i][j]);
        }
    }
    nxt = 0, pre = 0;
    for (int i = 1; i <= n; i++) {
        nxt += i + m - 1;
        for (int j = 1; j <= m + i-1; j++) {
            add(pre + j, pre + j + total, 1, num[i][j]);
            if (i == n)continue;
            add(pre + j+total, nxt + j, 1, 0);
            add(pre + j+total, nxt + j + 1, 1, 0);
        }
        pre = nxt;
    }
    for (int i = 1; i <= m; i++) {
        add(s, i, 1, 0);
    }
    for (int i = total - (m + n - 1) + 1; i <= total; i++) {
        add(i + total, t, 1, 0);
    }
    mcmf();
    printf("%lld\n", mincost);
    memset(hd, 0, sizeof(hd)); tot = 1;
    nxt = 0, pre = 0;
    for (int i = 1; i <= n; i++) {
        nxt += i + m - 1;
        for (int j = 1; j <= m + i - 1; j++) {
            add(pre + j, pre + j + total, inf, num[i][j]);
            if (i == n)continue;
            add(pre + j + total, nxt + j, 1, 0);
            add(pre + j + total, nxt + j + 1, 1, 0);
        }
        pre = nxt;
    }
    for (int i = 1; i <= m; i++) {
        add(s, i,1, 0);
    }
    for (int i = total - (m + n - 1) + 1; i <= total; i++) {
        add(i + total, t, inf, 0);
    }
    mcmf();
    printf("%lld\n", mincost);
    //dp
    /*for (int i = n; i>=1 ; i--) {
        for (int j = 1; j <= m + i - 1; j++) {
            res[i][j] = max(res[i + 1][j], res[i + 1][j + 1]) + num[i][j];
        }
    }
    ll ans = 0;
    for (int i = 1; i <= m; i ++) {
        ans += res[1][i];
    }
    printf("%lld\n", ans);*/
    memset(hd, 0, sizeof(hd)); tot = 1;
    nxt = 0, pre = 0;
    for (int i = 1; i <= n; i++) {
        nxt += i + m - 1;
        for (int j = 1; j <= m + i - 1; j++) {
            add(pre + j, pre + j + total, inf, num[i][j]);
            if (i == n)continue;
            add(pre + j + total, nxt + j, inf, 0);
            add(pre + j + total, nxt + j + 1, inf, 0);
        }
        pre = nxt;
    }
    for (int i = 1; i <= m; i++) {
        add(s, i, 1, 0);
    }
    for (int i = total - (m + n - 1) + 1; i <= total; i++) {
        add(i + total, t, inf, 0);
    }
    mcmf();
    printf("%lld\n", mincost);
    return 0;
}

 

上一篇:opencv-python视频处理之视频慢动作和视频漫画风格


下一篇:控制流程系列教材 (三)- java的while语句