P4013 数字梯形问题 [最小费用最大流问题]

P4013 数字梯形问题

中文题干
三个情况,就是三种建图的方式,而且三种建图越来越简单。
虽说要建的图不太一样,但是本质上只是拆点不拆点,以及边的权值和流量选择的问题

脑洞:
三个规则:
第一种::: 所有的点和边都不能相交,我们要进行拆点操作,每个点第一部分连接到上一层的点(第一层就连接源点)。每个点的第二部分连接到下一层的点(如果是最后一层就连接到汇点上去)。中间的所有边,他的流量都是一,因为都只能走一次。而这些边中,只有同一个点的两个部分相互连接的那条边才有权值,为这个点的权值。
第二种:::不拆点啦,但是所有的边还都只能走一次,所以,除了拆边操作意外,其他的都是一行的。至于权值,我们就赋到每个点的后面一条边,即从这个点出发到达另外的点的那条边,这样才知道我们走的是哪条边。
第三种::: 这种就更加简单了呃, 边都可以重复走,那我们就全部给成INF就行了,但要注意的是,源点到第一层上的点的这些边的流量永远是1。

嗯嗯 ,就这样, 代码建图都是复制粘贴,改变一些细节。。。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>

#define len 100010
#define MAXN 2020
#define INF 0x3f3f3f3f

using namespace std;

struct edge{
    int u, v, f, c, nxt;
};

edge e[len];
int head[MAXN], hcnt;
int dis[MAXN], inq[MAXN];
int pre[len], M[MAXN][MAXN];
int S, T;
int m, n;

void Init(){
    memset(head, -1, sizeof (head));
    hcnt = 0;
}

void adde(int u, int v, int f, int c){
    e[hcnt].u = u; e[hcnt].v = v; e[hcnt].c = c; e[hcnt].f = f;
    e[hcnt].nxt = head[u]; head[u] = hcnt ++;
    e[hcnt].u = v; e[hcnt].v = u; e[hcnt].c = -c; e[hcnt].f = 0;
    e[hcnt].nxt = head[v]; head[v] = hcnt ++;
}

bool spfa(){
    memset(dis, INF, sizeof (dis));
    memset(inq, 0, sizeof (inq));
    memset(pre, -1, sizeof (pre));
    queue <int> q;
    dis[S] = 0; q.push(S);
    inq[S] ++;
    
    while (!q.empty()){
        int h = q.front(); q.pop();
        
        for (int i=head[h]; ~i; i=e[i].nxt){
            int v = e[i].v;
            if (dis[v] > dis[h] + e[i].c && e[i].f > 0){
                dis[v] = dis[h] + e[i].c;
                pre[v] = i;
                if (!inq[v]){
                    inq[v] ++;
                    q.push(v);
                }
            }
        }
        inq[h] = 0;
    }
    if (pre[T] == -1) return false;
    return true;
}

int dfs(){
    int res = 0;
    while (spfa()){
        int d = INF;
        for (int i=pre[T]; ~i; i=pre[e[i].u])
            if (d > e[i].f) d = e[i].f;
        
        for (int i=pre[T]; ~i; i=pre[e[i].u]){
            e[i].f -= d;
            e[i^1].f += d;
            res += d * e[i].c;
        }
    }
    return res;
}

int get(int i, int j){
    return (m + m + i - 2) * (i - 1) / 2 + j;
}

int main(){
    scanf("%d%d", &m, &n);
    Init();
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m + i - 1; j++)
            scanf("%d", &M[i][j]);
    //first:::
    int sum = (m+m+n-1)*n/2;
    S = 0; T = sum * 2 + 1;
    for (int i=1; i<=n; i++){
        for (int j=1; j<=m+i-1; j++){
            adde(get(i, j), get(i, j) + sum, 1, -M[i][j]);
            if (i == 1)
                adde(S, get(i, j), 1, 0);
            if (i < n){
                adde(sum + get(i, j), get(i+1, j), 1, 0);
                adde(sum + get(i, j), get(i+1, j+1), 1, 0);
            }
            if (i == n)
                adde(sum + get(i, j), T, 1, 0);
        }
    }
    int ans = dfs();
    printf ("%d\n", -ans);
    
    //second:::
    Init();
    S = 0; T = sum + 1;
    for (int i=1; i<=n; i++){
        for (int j=1; j<=m+i-1; j++){
            if (i == 1)
                adde(S, get(i, j), 1, 0);
            if (i < n){
                adde(get(i, j), get(i+1, j), 1, -M[i][j]);
                adde(get(i, j), get(i+1, j+1), 1, -M[i][j]);
            }
            if (i == n)
                adde(get(i, j), T, INF, -M[i][j]);
        }
    }
    ans = dfs();
    printf ("%d\n", -ans);
    
    //third:::
    Init();
    S = 0; T = sum + 1;
    for (int i=1; i<=n; i++){
        for (int j=1; j<=m+i-1; j++){
            if (i == 1)
                adde(S, get(i, j), 1, 0);
            if (i < n){
                adde(get(i, j), get(i+1, j), INF, -M[i][j]);
                adde(get(i, j), get(i+1, j+1), INF, -M[i][j]);
            }
            if (i == n)
                adde(get(i, j), T, INF, -M[i][j]);
        }
    }
    ans = dfs();
    printf ("%d\n", -ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值