P2765 魔术球问题

题目描述

«问题描述:

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

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

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

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

«编程任务:

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

输入格式

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

输出格式

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

输入输出样例

输入 #1复制

4

输出 #1复制

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

说明/提示

4<=n<=55

思路:

首先知道放球的个数和柱子的个数是成正相关的,

一直增加球,对于每一个球i,将比其小的,能和其相加为平方数的,统一连中间边。

之所以叫中间边,是因为这题要拆点,设源点和汇点分别为S和T。

将每一个点i,拆为 Xi,Yi,

每一个点将其S与Xi连接,Yi与T连接,

中间点就是如果i节点和j节点相连接,

就Xi 与Yj,相连,。以上讲到的边,流量全为1.

当num个数建立的容量网络的最小边覆盖x>n时,即n个柱子无法覆盖掉num个数时,

结束加边操作,此时num-1,就是N个柱子能覆盖到的最大数字。

在dinic算法中增光路的过程中,记录每一个v,增广的下一个节节点u。

一路链状输出就是方法。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}

inline void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
const int INF = (1 << 30);
const int MAXN = 400000;
int idx = 0, e[MAXN], f[MAXN], ne[MAXN], h[100000];
void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], f[idx] = c, h[a] = idx++;
    e[idx] = a, ne[idx] = h[b], f[idx] = 0, h[b] = idx++;
}
int S, T, ch[MAXN], q[MAXN], nex[MAXN];
bool tell() {
    memset(ch, -1, sizeof(ch));
    int head = 0, tail = 0;
    ch[q[0] = S] = 0;
    while (head <= tail) {
        int t = q[head++];
        for (int i = h[t]; i != -1; i = ne[i]) {
            if (ch[e[i]] == -1 && f[i]) {
                ch[q[++tail] = e[i]] = ch[t] + 1;
            }
        }
    }
    return ch[T] != -1;
}
int zeng(int a, int b) {
    if (a == T)return b;
    int r = 0;
    for (int i = h[a]; i != -1; i = ne[i]) {
        if (ch[a] + 1 == ch[e[i]] && f[i]) {
            int t = zeng(e[i], min(b - r, f[i]));
            if (t > 0)
            {
                // 可找路径。
                nex[a >> 1] = (e[i] >> 1);
            }
            f[i] -= t; r += t; f[i ^ 1] += t;
        }
    }
    if (!r)ch[a] = -1;
    return r;
}
int dinic() {
    int r = 0, t = 0;
    while (tell()) {
        while (t = zeng(S, INF)) {
            r += t;
        }
    }
    return r;
}
void set_S_T(int s, int t)
{
    S = s;
    T = t;
}
void init()
{
    memset(h, -1, sizeof(h));
    memset(nex, -1, sizeof(nex));
}
int n;
int w[1000];
bool vis[maxn];
int main()
{
    //freopen("D:\\code\\text\\input.txt","r",stdin);
    //freopen("D:\\code\\text\\output.txt","w",stdout);
    gbtb;
    init();
    S = 0;
    T = 1e4 + 10;
    cin >> n;
    int now = 0;
    int num = 0;
    while (now <= n)
    {
        ++num;
        add(S, num << 1, 1);
        add((num << 1) | 1, T, 1);
        for (int i = sqrt(num) + 1; i * i < (num << 1); ++i)
        {
            add((i * i - num) << 1, (num << 1) | 1, 1);
        }
        int s = dinic();
        if (!s)
        {
            w[++now] = num;
        }
    }
    cout << num - 1 << endl;
    int k;
    repd(i, 1, n)
    {
        if (!vis[w[i]])
        {
            k = w[i];
            cout << k;
            vis[k] = 1;
            while (nex[k] != -1 && nex[k] != (T) >> 1 )
            {
                k=nex[k];
                vis[k]=1;
                cout<<" "<<k;
            }
            cout<<endl;
        }
    }
    return 0;
}

inline void getInt(int* p) {
    char ch;
    do {
        ch = getchar();
    } while (ch == ' ' || ch == '\n');
    if (ch == '-') {
        *p = -(getchar() - '0');
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 - ch + '0';
        }
    }
    else {
        *p = ch - '0';
        while ((ch = getchar()) >= '0' && ch <= '9') {
            *p = *p * 10 + ch - '0';
        }
    }
}