zoukankan      html  css  js  c++  java
  • 洛谷P1004 方格取数

    网络流大法吼

    不想用DP的我选择了用网络流……

    建模方法:

    从源点向(1,1)连一条容量为2(走两次),费用为0的边

    从(n,n)向汇点连一条容量为2,费用为0的边

    每个方格向右边和下边的方格连一条容量为inf,费用为0的边

    走到每个方格,会取出方格上的数。每个方格的数只会被取走一次。

    于是我们考虑拆点

    每个方格向拆出的点连一条容量为1(只能被取走一次),费用为方格上的数的边

    由于每个方格不一定只走一次,所以再连一条容量为inf,费用为0的边

    然后跑最大费用最大流就行了~


    我的代码中把边上的费用取相反数,跑最小费用最大流,最后得出的最小费用取负就是答案啦~!

    下面是代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 210;
    
    struct edge {
        int flow, cap, cost, t, next;
    }e[maxn * 50];
    int n, m, s, t, p = -1, head[maxn], maxflow, mincost = 0, pre[maxn], dis[maxn], a[maxn][maxn];
    bool vis[maxn];
    
    void add_edge(int s, int t, int cap, int cost) {
        p++;
        e[p].t = t;
        e[p].cap = cap;
        e[p].cost = cost;
        e[p].next = head[s];
        head[s] = p;
    }
    
    bool spfa() {
        queue < int > q;
        while(!q.empty())q.pop();
        memset(vis, 0, sizeof(vis));
        vis[s] = 1;
        q.push(s);
        memset(dis, 0x7f, sizeof(dis));
        memset(pre, -1, sizeof(pre));
        dis[s] = 0;
        while(!q.empty()) {
            int k = q.front();
            q.pop();
            vis[k] = false;
            for(int i = head[k]; i != -1; i = e[i].next) {
                if(e[i].cap && dis[e[i].t] > dis[k] + e[i].cost) {
                    dis[e[i].t] = dis[k] + e[i].cost;
                    pre[e[i].t] = i;
                    if(!vis[e[i].t]) {
                        vis[e[i].t] = true;
                        q.push(e[i].t);
                    }
                }
            }
        }
        // for(int i = 0; i <= n * n; i++) cout << dis[i] << " ";
        // cout << dis[t] << endl;
        if(dis[t] == 0x7f7f7f7f) return false;
        else return true;
    }
    
    void MCMF() {
        while(spfa()) {
            int mf = 0x7fffffff;
            for(int i = pre[t]; i != -1; i = pre[e[i ^ 1].t]) {
                mf = min(mf, e[i].cap);
                // cout << i << " " << pre[e[i ^ 1].t] << endl;
                // cout << e[2].cap << endl;
            }
            maxflow += mf;
            for(int i = pre[t]; i != -1; i = pre[e[i ^ 1].t]) {
                e[i].cap -= mf;
                e[i ^ 1].cap += mf;
            }
            mincost += mf * dis[t];
        }
    }
    
    int pos(int x, int y) {
        return (x - 1) * n + y;
    }
    
    int main() {
        memset(head, -1, sizeof(head));
        cin >> n;
        s = 0, t = maxn - 3;
        int x, y, z;
        while(1) {
            cin >> x >> y >> z;
            if(!(x || y || z)) break;
            a[x][y] = z;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                add_edge(pos(i, j), pos(i, j) + n * n, 1, -a[i][j]);
                add_edge(pos(i, j) + n * n, pos(i, j), 0, a[i][j]);
                add_edge(pos(i, j), pos(i, j) + n * n, 0x7fffffff, 0);
                add_edge(pos(i, j) + n * n, pos(i, j), 0, 0);
                if(i < n) {
                    add_edge(pos(i, j) + n * n, pos(i + 1, j), 0x7fffffff, 0);
                    add_edge(pos(i + 1, j), pos(i, j) + n * n, 0, 0);
                }
                if(j < n) {
                    add_edge(pos(i, j) + n * n, pos(i, j + 1), 0x7fffffff, 0);
                    add_edge(pos(i, j + 1), pos(i, j) + n * n, 0, 0);
                }
            }
        }
        add_edge(s, pos(1, 1), 2, 0);
        add_edge(pos(1, 1), s, 0, 0);
        add_edge(pos(n, n) + n * n, t, 2, 0);
        add_edge(t, pos(n, n) + n * n, 0, 0);
        MCMF();
        cout << -mincost << endl;
        return 0;
    }
    

    qwq

  • 相关阅读:
    KindEditor编辑器的使用
    导航栏
    ajax php 验证注册用户名是否存在
    PS照片改底色
    截取邮箱@后面内容的两种情况
    js导航栏单击事件背景颜色变换
    model中的自动验证 $_validate $_auto
    一对一关联模型,HAS_ONE
    一对多关联模型,BELONGS_TO
    C++操作MySQL数据库
  • 原文地址:https://www.cnblogs.com/iycc/p/10369420.html
Copyright © 2011-2022 走看看