zoukankan      html  css  js  c++  java
  • 【网络流24题】 4. 魔术球问题 题解

    题目链接(洛谷 P2765)

    题意

    假设有(n)根柱子,现要按下述规则在这(n)根柱子中依次放入编号为(1, 2, 3, cdots)的球:

    • 每次只能在某根柱子的最上面放球。
    • 同一根柱子中,任何(2)个相邻球的编号之和为完全平方数。

    试设计一个算法,计算出在(n)根柱子上最多能放多少个球。例如,在(4)根柱子上最多可放(11)个球。对于给定的(n),计算在(n)根柱子上最多能放多少个球。

    思路

    贪心

    对于这道题,我很自然地想到了一个贪心策略。首先,很容易发现,这道题柱子的编号其实根本不重要。那么,我们规定,相同条件下放球时,优先放入编号小的柱子。

    当我们放入第(k)个球时,我们采用如下贪心策略:

    • (1)(n)扫描所有柱子。如果(k)与某个柱子顶端的球数字之和为完全平方数,则放入这个柱子;
    • 若所有放入过球的柱子都不能放入,则找到下一个没有放入球的柱子放进去;
    • 否则无法放入这个球,输出结果,退出。

    正确性

    若一个球既可以放入某个有球的柱子,也可以放入某个无球的柱子,那么,放入有球的柱子一定更优。因为将(k)放入有球的柱子,会出现一个顶端为(k)的柱子和一个空柱子(就是没放进去的那个),而放入空柱子则会出现一个顶端为(k)的柱子和一个能与(k)匹配的柱子,显然前者比后者更优。

    不会出现一个数放入两个已经有球的柱子的情况。因为投入球的顺序一定是从(1)(n)逐个递增的,按照上述投入球的规则,最大的若干个球一定都在各个最外侧,而且它们的数一定是连续的。(可以按照上述规则手推一下(n = 4)的情况)

    网络流

    其实个人感觉归为网络流不是很合适,因为把本题抽象成图的做法,本质上是做二分图匹配。关于图的点数的通项,并不是我自己想出来的,也无法给出详细的证明,所以建议优先参考贪心做法。

    给出结论:若一共有(n)个柱子,则总共能放入(leftlfloor dfrac{n cdot (n + 2) + (n mod 2) - 2}{2} ight floor)个球。

    然后,我们很容易想到,我们尽可能把临近的能组成完全平方数的数穿起来,比如(1 ightarrow 3 ightarrow 6),那么,一个这样的串上的数就能都放到一个柱子上。同时,我们希望这样的串尽可能少。

    有没有觉得很熟悉?希望串尽可能少,剩下的部分就和第三题最小路径覆盖问题一样了。

    然后,我们能推算出一共有多少球,这样,我们就能构图了,剩下的就同第三题——最小路径覆盖问题一样了。

    代码

    贪心

    /**
     * luogu P2765 https://www.luogu.com.cn/problem/P2765
     * 贪心
     **/
    
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 10000;
    vector<int> s[maxn];
    int n, ans, d;
    
    int check(const int &x) {	// 判断球x能否放入,能放入则返回放入的柱子编号,否则返回0
        for (int i = 1; i <= n; i++) {
            if (s[i].empty()) return i;
            int t = (int)sqrt(s[i].back() + x);
            if (t * t == (s[i].back() + x))
                return i;
        }
        return 0;
    }
    
    int main() {
        scanf("%d", &n);
        while ((d = check(ans + 1))) {
            if (d == 0) break;
            ans++;
            s[d].push_back(ans);
        }
        printf("%d
    ", ans);
        for (int i = 1; i <= n; i++) {
            for (auto j : s[i])
                printf("%d ", j);
            putchar('
    ');
        }
        return 0;
    }
    

    网络流

    /**
     * luogu P2765 https://www.luogu.com.cn/problem/P2765
     * 网络流
     **/
    
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    
    using namespace std;
    const int maxn = 5000;
    const int maxm = 1e5 + 5;
    const int INF = 0x3f3f3f3f;
    const int S = 0;
    const int T = maxn - 1;
    
    struct Edge {
        int to, nxt, val;
    }e[maxm];
    
    int numedge, head[maxn], n, pre[maxn], nxt[maxn], depth[maxn];
    bool ispfs[maxn];
    
    inline void AddEdge(int from, int to, int val) {
        e[numedge].to = to;
        e[numedge].val = val;
        e[numedge].nxt = head[from];
        head[from] = numedge;
        numedge++;
    }
    
    inline bool bfs() {
        memset(depth, 0, sizeof(depth));
        depth[S] = 1;
        queue<int> q;
        q.push(S);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = e[i].nxt) {
                int to = e[i].to;
                if (!depth[to] && e[i].val > 0) {
                    depth[to] = depth[u] + 1;
                    q.push(to);
                }
            }
        }
        return depth[T] != 0;
    }
    
    inline int dfs(int u, int flow) {
        if (u == T) return flow;
        for (int i = head[u]; ~i; i = e[i].nxt) {
            int to = e[i].to;
            if (depth[to] > depth[u] && e[i].val > 0) {
                int di = dfs(to, min(flow, e[i].val));
                if (di > 0) {
                    if (to > n) {
                        pre[to - n] = u;
                        nxt[u] = to - n;
                    }
                    e[i].val -= di;
                    e[i ^ 1].val += di;
                    return di;
                }
            }
        }
        return 0;
    }
    
    int Dinic() {
        int res = 0;
        while (bfs()) {
            int d = 0;
            while ((d = dfs(S, INF))) {
                res += d;
            }
        }
        return res;
    }
    
    int main() {
        memset(head, -1, sizeof(head));
        for (int i = 1; i * i < maxn; i++)
            ispfs[i * i] = true;    // 用来判断i是否是完全平方数
    
        scanf("%d", &n);
        n = (n * (n + 2) + (n & 1) - 2) / 2;    // 很迷的通项公式
        for (int i = 1; i <= n; i++) pre[i] = nxt[i] = i;
    
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (ispfs[i + j]) {
                    AddEdge(i, j + n, 1);
                    AddEdge(j + n, i, 0);
                }
            }
        }
    
        for (int i = 1; i <= n; i++) {
            AddEdge(S, i, 1);
            AddEdge(i, S, 0);
            AddEdge(i + n, T, 1);
            AddEdge(T, i + n, 0);
        }
        Dinic();
        printf("%d
    ", n);
        for (int i = 1; i <= n; i++) {
            if (pre[i] == i) {
                int u = i;
                for (u = i; nxt[u] != u; u = nxt[u])
                    printf("%d ", u);
                printf("%d
    ", u);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    【LeetCode】048. Rotate Image
    【LeetCode】036. Valid Sudoku
    【LeetCode】060. Permutation Sequence
    【LeetCode】001. Two Sum
    【LeetCode】128. Longest Consecutive Sequence
    【LeetCode】081. Search in Rotated Sorted Array II
    【LeetCode】033. Search in Rotated Sorted Array
    顺时针打印矩阵
    矩形覆盖
    二维数组中的查找
  • 原文地址:https://www.cnblogs.com/icysky/p/13610948.html
Copyright © 2011-2022 走看看