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;
    }
    
  • 相关阅读:
    jQuery禁用或启用
    ASP.NET MVC一次删除多笔记录
    在你的ASP.NET MVC中使用查找功能
    Get radio selected value
    绑定一个值给radio
    ASP.NET MVC实现权限控制
    为Guid数据类型的属性(property)赋值
    Razor语法中绑定一个值给checkbox
    判断IEnumerable<T>集合中是否包含有T对象
    SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
  • 原文地址:https://www.cnblogs.com/icysky/p/13610948.html
Copyright © 2011-2022 走看看