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';
}
}
}