[Luogu P2765] [网络流24题] 魔术球问题

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/84580575

洛谷传送门

题目描述

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

(1)每次只能在某根柱子的最上面放球。

(2)在同一根柱子中,任何 2 2 个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在 n n 根柱子上最多能放多少个球。例如,在 4 4 根柱子上最多可放 11 11 个球。

对于给定的 n n ,计算在 n n 根柱子上最多能放多少个球。

输入输出格式

输入格式:

第1 行有 1 1 个正整数 n n ,表示柱子数。

输出格式:

程序运行结束时,将 n n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的 n n 行,每行是一根柱子上的球的编号。

输入输出样例

输入样例#1:

4

输出样例#1:

11
1 8
2 7 9
3 6 10
4 5 11

说明

感谢 @PhoenixEclipse 提供spj

4 n 55 4\le n\le 55

解题分析

先考虑确定一个 k k , 我们如何判定需要几个柱子。

很显然类似路径覆盖的思想, 我们从小的数向大的数连边, 那么柱子的数量等于 k k- 匹配数。

我们可以动态加点, 跑到柱子数大于 n n 的时候, D F S DFS 每个数, 寻找流量为 1 1 的反向边, 然后 p u s h push v e c t o r vector 里正向输出即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <cctype>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 3600
#define INF 1e8
#define S 0
#define T 3500
#define EPS 1e-8
#define BASE 1700
int n, cnt;
int head[MX], layer[MX];
bool vis[MX];
std::vector <int> vec[MX];
struct Edge {int to, fl, nex;} edge[MX * MX << 1];
template <class C> IN C max(C a, C b) {return a > b ? a : b;}
template <class C> IN C min(C a, C b) {return a < b ? a : b;}
IN void add(R int from, R int to, R int fl)
{
    edge[++cnt] = {to, fl, head[from]}, head[from] = cnt;
    edge[++cnt] = {from, 0, head[to]}, head[to] = cnt;
}
namespace Dinic
{
    std::queue <int> q;
    IN bool BFS()
    {
        std::memset(layer, 0, sizeof(layer));
        layer[S] = 1; q.push(S); R int now;
        W (!q.empty())
        {
            now = q.front(); q.pop();
            for (R int i = head[now]; ~i; i = edge[i].nex)
            {
                if (edge[i].fl && (!layer[edge[i].to]))
                layer[edge[i].to] = layer[now] + 1, q.push(edge[i].to);
            }
        }
        return layer[T];
    }
    int DFS(R int now, R int avai)
    {
        if (now == T) return avai;
        R int lef = avai, buf;
        for (R int i = head[now]; ~i; i = edge[i].nex)
        {
            if (edge[i].fl && layer[edge[i].to] == layer[now] + 1)
            {
                buf = DFS(edge[i].to, min(lef, edge[i].fl));
                if (!buf) continue;
                lef -= buf, edge[i].fl -= buf, edge[i ^ 1].fl += buf;
                if (!lef) return avai;
            }
        }
        return avai - lef;
    }
    int solve()
    {
        int ret = 0;
        W (BFS()) ret += DFS(S, INF);
        return ret;
    }
}
void check(R int now, int &id)
{
    vis[now] = true;
    bool flag = false;
    for (R int i = head[now + BASE]; i; i = edge[i].nex)
    {
        if (i % 2 && edge[i].fl)
        {
            flag = true;
            check(edge[i].to, id);
            break;
        }
    }
    if (!flag) return id = now, vec[now].push_back(now), void();
    else vec[id].push_back(now);
}
IN bool isok(R int i, R int j)
{return fabs(std::sqrt(i + j) - (int)std::sqrt(i + j)) < EPS;}
int main(void)
{
    int foo;
    std::memset(head, cnt = -1, sizeof(head));
    scanf("%d", &n);
    R int lim, tot = 0;
    for (lim = 1; ; ++lim)
    {
        add(S, lim, 1);
        add(lim + BASE, T, 1);
        for (R int i = 1; i < lim; ++i) if(isok(i, lim)) add(i, lim + BASE, 1);
        tot += Dinic::solve();
        if (lim - tot > n) {--lim; break;}
    }
    printf("%d\n", lim);
    for (R int i = lim; i; --i)
    if (!vis[i]) check(i, foo);
    for (R int i = 1; i <= lim; ++i) if (vec[i].size())
    {for (R int j = 0; j < vec[i].size(); ++j) printf("%d ", vec[i][j]); puts("");}
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/84580575