假设有 n n n根柱子,现要按下述规则在这 n n n根柱子中依次放入编号为 1 1 1, 2 2 2, 3 3 3,…的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何 2 2 2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在 n n n根柱子上最多能放多少个球。例如,在 4 4 4 根柱子上最多可放 11 11 11 个球。
对于给定的 n n n,计算在 n n n根柱子上最多能放多少个球。
第1 行有 1 1 1个正整数 n n n,表示柱子数。
程序运行结束时,将 n n n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的 n n n 行,每行是一根柱子上的球的编号。
4
11
1 8
2 7 9
3 6 10
4 5 11
感谢 @PhoenixEclipse 提供spj
4 ≤ n ≤ 55 4\le n\le 55 4≤n≤55
先考虑确定一个 k k k, 我们如何判定需要几个柱子。
很显然类似路径覆盖的思想, 我们从小的数向大的数连边, 那么柱子的数量等于 k − k- k−匹配数。
我们可以动态加点, 跑到柱子数大于 n n n的时候, D F S DFS DFS每个数, 寻找流量为 1 1 1的反向边, 然后 p u s h push push到 v e c t o r vector vector里正向输出即可。
代码如下:
#include
#include
#include
#include
#include
#include
#include
#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("");}
}