zoukankan      html  css  js  c++  java
  • 网络流24题(05)魔术球问题(最小路径覆盖 + 最大流)

    思路:

    1. 要求相邻的 2 个球数字相加为完全平方数,则相当于从 1 开始构造出来一个有向无环图:1->3->6..

    2. 模型就变成了“最小路径覆盖”的问题,找图中最少的路径数,这里的路径数就是题目的柱子数。继而建模成了“二分图匹配”求最大流的问题;

    3. 因为对于数字的未知性,本题采取了枚举的方法,不过每次都是在上一次最大流的基础上继续增广;

    4. 代码中需要用到一个很强的剪纸:在红色部分。正常 dinic 求最大流是不需要这句话的,因为增广的时候是允许流“撤销”的,但是本题

       不需要流撤销自己的错误也能达到正确的结果,仔细想想可能是因为这个有向图的原因,公共节点把图分隔开,始终都是一样的;

    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <vector>
    using namespace std;
    
    const int MAXN = 4010;
    const int OFFSET = 2000;
    const int INFS = 0x3FFFFFFF;
    
    struct edge {
        int from, to, cap, flow;
        edge(int _from, int _to, int _cap, int _flow)
            : from(_from), to(_to), cap(_cap), flow(_flow) {}
    };
    
    class Dinic {
    public:
        void addedge(int u, int v, int cap) {
            edges.push_back(edge(u, v, cap, 0));
            edges.push_back(edge(v, u, 0, 0));
            int m = edges.size();
            G[u].push_back(m - 2);
            G[v].push_back(m - 1);
        }
        bool BFS() {
            memset(vis, false, sizeof(vis));
            memset(d, 0, sizeof(d));
    
            queue<int> Q;
            Q.push(s);
            vis[s] = true;
            while (!Q.empty()) {
                int x = Q.front(); Q.pop();
                for (int i = 0; i < G[x].size(); i++) {
                    edge& e = edges[G[x][i]];
                    if (!vis[e.to] && e.cap > e.flow && e.cap > 0) {
                        vis[e.to] = true;
                        d[e.to] = d[x] + 1;
                        Q.push(e.to);
                    }
                }
            }
            return vis[t];
        }
        int DFS(int x, int aug) {
            if (x == t || aug == 0) return aug;
            int flow = 0;
            for (int i = 0; i < G[x].size(); i++) {
                edge& e = edges[G[x][i]];
                if (d[e.to] == d[x] + 1) {
                    int f = DFS(e.to, min(aug, e.cap-e.flow));
                    if (f == 0) continue;
                    e.flow += f;
                    edges[G[x][i]^1].flow -= f;
                    flow += f;
                    aug -= f;
                    if (aug == 0) break;
                }
            }
            return flow;
        }
        int maxflow(int s, int t) {
            this->s = s, this->t = t;
            int flow = 0;
            while (BFS()) {
                flow += DFS(s, INFS);
            }
            return flow;
        }
        void cleardata(int n) {
            this->n = n;
            edges.clear();
            for (int i = 0; i < n; i++)
                G[i].clear();
        }
        void print(int x) {
            vis[x] = true;
            for (int i = 0; i < G[x].size(); i++) {
                edge& e = edges[G[x][i]];
                if (!vis[e.to] && e.flow == 1 && e.to != t) {
                    printf(" %d", e.to - OFFSET); print(e.to - OFFSET);
                    break;
                }
            }
    
        }
        void printpath(int num) {
            memset(vis, false, sizeof(vis));
            for (int x = 1; x <= num; x++) {
                if (!vis[x]) { 
                    printf("%d", x); print(x); printf("\n");
                }
            }
        }
    private:
        vector<edge> edges;
        vector<int> G[MAXN];
        int d[MAXN], s, t, n;
        bool vis[MAXN];
    };
    
    Dinic dc;
    bool issquare[MAXN];
    
    void initdata() {
        memset(issquare, false, sizeof(issquare));
        for (int i = 1; i <= 60; i++)
            issquare[i*i] = true;
    }
    
    int main() {
        initdata();
    
        int n;
        scanf("%d", &n);
    
        int flow = 0, num = 0;
        int s = 0, t = OFFSET*2 + 1;
    
        dc.cleardata(t + 1);
    
        while (num - flow <= n) {
            num += 1;
            dc.addedge(s, num, 1);
            dc.addedge(num + OFFSET, t, 1);
            for (int i = 1; i < num; i++)
                if (issquare[i+num])
                    dc.addedge(i, num + OFFSET, 1);
    
            flow += dc.maxflow(s, t);
        }
        num -= 1;
    
        dc.cleardata(t + 1);
        for (int i = 1; i <= num; i++) {
            dc.addedge(s, i, 1);
            dc.addedge(i + OFFSET, t, 1);
            for (int j = 1; j < i; j++)
                if (issquare[j+i])
                    dc.addedge(j, i + OFFSET, 1);
        }
        dc.maxflow(s, t);
        printf("%d\n", num);
        dc.printpath(num);
    
        return 0;
    }
  • 相关阅读:
    Python实例1-Collatz 序列
    Git 教程
    python 算法基础
    认识 flask框架 及 介绍
    python 虚拟环境
    Numpy基本操作学习
    Jupyter Notebook 快捷键指南
    Gallery——Matplotlib
    list.pop()函数操作头部与尾部的计时试验
    list 与 dict 的in操作比较试验
  • 原文地址:https://www.cnblogs.com/kedebug/p/3029803.html
Copyright © 2011-2022 走看看