wiki 1913 数字梯形问题

这是一道很经典的费用流题目,可以通过最大费用最大流实现
通过分析题目,我们很容易想到建模方法:

1、对于规则一,题目规定每一个节点只能访问一次,也就是说,实际上这个时候每一个点都有容量限制,所以我们必须把点i拆分成两个点Xi,Yi。此时每一条边的容量都是1,费用为改点的权值,此时只允许使用该节点一次,并且代价为权值。数字梯形是逐层向下的,并且每一个点可以向下面两个方向进行扩展,于是我们在这些点之间建立容量为1,费用为0的边。对于最顶层与最底层,我们分让两层分别与源点和汇点相连,容量同样是1,费用为0。(注意所有的边都是有向边,并且结点i与j相连是指Yi与Xj相连)

2、对于规则二,由于可以共点,所以就不用拆点了,源点与上层继续保持容量为1,其他的关联除了不用拆点外,其他都差不多,注意一下底层与汇点的容量限制改为无穷大即可。

3、对于规则三,由于边和节点都可以共用,此时没有必要把原网络销毁,直接在上一个规则的前提下修改每一条边的容量即可(注意源点与顶层节点之间的容量是不能变的,否则就无法满足是m条路径取得的最大值,其他边的容量改成无穷大即可)


上面是我摘录的别人的题解,我的代码跟这个题解思路完全一致,我的AC代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<iomanip>
#include<queue>
#include<cmath>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<algorithm>

using namespace std;
const int INF = 1<<30;
const int maxN = 2000;
int m,n,s,t,result;
int mymap[50][50];
struct Edge{
    int from, to, cap, flow, cost;
};
vector<Edge> es;
vector<int> g[maxN];
int pos[50][50][2];
int dir[2][2] = {1,0,1,1};
void addedge (int from, int to, int cap, int cost){
    es.push_back(Edge{from, to, cap, 0, cost});
    es.push_back(Edge{to, from, 0, 0, -cost});
    int u = es.size();
    g[from].push_back(u-2);
    g[to].push_back(u-1);
}
void init(){
    cin >> m >> n;
    memset(mymap, -1, sizeof(mymap));
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            cin >> mymap[i][j];
        }
    }
}
void make1(){
    memset(pos, -1, sizeof(pos));
    int sz = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            pos[i][j][0] = sz++;
            pos[i][j][1] = sz++;
        }
    }
    s = sz++;
    t = sz++;
    for(int i = 0; i < m; i++) addedge(s, pos[0][i][0], 1, 0);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            addedge(pos[i][j][0], pos[i][j][1], 1, -mymap[i][j]);
            for(int u = 0; u < 2; u++){
                int x = i + dir[u][0];
                int y = j + dir[u][1];
                if(x < n){
                    addedge(pos[i][j][1], pos[x][y][0], 1, 0);
                }
            }
        }
    }
    for(int i = 0; i < m+n-1; i++){
        addedge(pos[n-1][i][1], t, 1, 0);
    }
}
void solve (){
    int a[maxN],d[maxN],p[maxN],inq[maxN];
    memset(a, 0, sizeof(a));
    a[s] = INF;
    while(true){
        for(int i = 0; i < maxN; i++) d[i] = INF;
        d[s] = 0;
        memset(inq, 0, sizeof(inq));
        queue<int> q;
        q.push(s);
        inq[s] = 1;
        while(!q.empty()){
            int u = q.front();
            q.pop();
            inq[u] = 0;
            for(int i = 0; i < g[u].size(); i++){
                Edge& e = es[g[u][i]];
                if(e.cap>e.flow && d[e.to] > d[e.from]+e.cost){
                    a[e.to] = (a[e.from] > e.cap-e.flow ? e.cap-e.flow : a[e.from]);
                    d[e.to] = d[e.from] + e.cost;
                    p[e.to] = g[u][i];
                    if(!inq[e.to]) {q.push(e.to); inq[e.to] = 1;}
                }
            }
        }
        if(d[t]==INF) break;
        result += -(d[t]*a[t]);
        int u = t;
        while(u!=s){
            es[p[u]].flow += a[t];
            es[p[u]^1].flow -= a[t];
            u = es[p[u]].from;
        }
    }
}
void make2(){
    memset(pos, -1, sizeof(pos));
    int sz = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            pos[i][j][0] = sz++;
        }
    }
    s = sz++;
    t = sz++;
    for(int i = 0; i < m; i++) addedge(s, pos[0][i][0], 1, -mymap[0][i]);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            for(int u = 0; u < 2; u++){
                int x = i + dir[u][0];
                int y = j + dir[u][1];
                if(x < n){
                    addedge(pos[i][j][0], pos[x][y][0], 1, -mymap[x][y]);
                }
            }
        }
    }
    for(int i = 0; i < m+n-1; i++){
        addedge(pos[n-1][i][0], t, INF, 0);
    }
}
void make3 (){
    memset(pos, -1, sizeof(pos));
    int sz = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            pos[i][j][0] = sz++;
        }
    }
    s = sz++;
    t = sz++;
    for(int i = 0; i < m; i++) addedge(s, pos[0][i][0], 1, -mymap[0][i]);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m+i; j++){
            for(int u = 0; u < 2; u++){
                int x = i + dir[u][0];
                int y = j + dir[u][1];
                if(x < n){
                    addedge(pos[i][j][0], pos[x][y][0], INF, -mymap[x][y]);
                }
            }
        }
    }
    for(int i = 0; i < m+n-1; i++){
        addedge(pos[n-1][i][0], t, INF, 0);
    }
}
int main(int argc, const char * argv[])
{
    init();
    es.clear();
    for(int i = 0; i < maxN; i++) g[i].clear();
    make1();
    solve();
    cout << result << endl;
    
    es.clear();
    for(int i = 0; i < maxN; i++) g[i].clear();
    result = 0;
    make2();
    solve();
    cout << result << endl;
    
    es.clear();
    for(int i = 0; i < maxN; i++) g[i].clear();
    result = 0;
    make3();
    solve();
    cout << result << endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值