题目描述
«问题描述:
假设有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
说明
感谢 @PhoenixEclipse 提供spj
4<=n<=55
解题思路
对于一个要放入的数字来说,这个数字有两种选择,一种是放在一个可以和他组成完全平方数的数字后面,另一种是放在一个新的柱子后面,这个时候用到的柱子数就+1了。自己设置一个源点s,一个汇点t。假设要放入一个数字x,那么可以把x拆分成两个点,x1和x2,让s到x1的容量为1,x2到t的容量为1。然后,找到所有比x小(已经被添加的数)并可以和x组成完全平方数的数y,让y1到x2的容量为1(x可以放在y后面),反向弧都设为0。那么,如果经过dinic()之后,弧(y1,x2)为饱和弧(选择了经过这条边),那就相当于是把x放在了y后面。如果x1的连边都没有饱和,那就说明x后面没有其他数字了,也就是说x是它所处木棍的最后一个。假设有r个像x一样的数字,那就说明使用了r个木棍。r(没有流量的点数) = 加入数字数 - 总流量。
代码如下
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
#include <cstring>
#define INF 0x3f3f3f
#define s 0
#define t 10000
#define maxn 1000
using namespace std;
struct Line{
int r, w;
bool dir;
Line(){ }
Line(int r, int w, bool d): r(r), w(w), dir(d){ }
};
int num[maxn];
vector<Line> line;
vector<int> g[t + 5];
int deep[t + 5];
bool bfs()
{
queue<int> que;
memset(deep, 0, sizeof(deep));
que.push(s);
deep[s] = 1;
while(!que.empty()){
int top = que.front();
que.pop();
for(int i = 0; i < g[top].size(); i ++){
int z = g[top][i];
if(!deep[line[z].r] && line[z].w){
deep[line[z].r] = deep[top] + 1;
que.push(line[z].r);
if(deep[t])
return true;
}
}
}
return false;
}
int dfs(int x, int mix)
{
if(x == t || !mix)
return mix;
int ap = 0;
for(int i = 0; i < g[x].size(); i ++){
int z = g[x][i];
if(deep[x] < deep[line[z].r] && line[z].w){
int p = dfs(line[z].r, min(mix, line[z].w));
mix -= p;
ap += p;
line[z].w -= p;
line[z^1].w += p;
if(!mix)
return ap;
}
}
return ap;
}
int dinic()
{
int ans = 0;
while(bfs())
ans += dfs(s, INF);
return ans;
}
void add_point(int x) //x-2*x, x`-2*x+1
{
line.push_back(Line(2*x, 1, 1));
g[s].push_back(line.size() - 1);
line.push_back(Line(s, 0, 0));
g[2*x].push_back(line.size() - 1);
line.push_back(Line(t, 1, 1));
g[2*x+1].push_back(line.size() - 1);
line.push_back(Line(2*x+1, 0, 0));
g[t].push_back(line.size() - 1);
for(int i = sqrt(x); i <= sqrt(x + x); i ++){
int l = num[i] - x;
if(l >= 1 && l < x){
line.push_back(Line(2*x+1, 1, 1));
g[2*l].push_back(line.size() - 1);
line.push_back(Line(2*l, 0, 0));
g[2*x+1].push_back(line.size() - 1);
}
}
}
bool vis[t + 5];
void dfs2(int x)
{
for(int i = 0; i < g[x].size(); i ++){
int z = g[x][i];
if(!vis[line[z].r / 2] && line[z].dir && !line[z].w){
cout << " " << line[z].r / 2;
vis[line[z].r / 2] = true;
dfs2(line[z].r - 1);
break;
}
}
}
int main()
{
int n;
for(int i = 0; i < maxn; i ++)
num[i] = i * i;
while(cin >> n){
int k = 0;
int w = 0;
do{
k++;
add_point(k);
w += dinic(); //+=
}while(k - w <= n);
cout << k - 1 << endl;
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= k - 1; i ++){
if(!vis[i]){
cout << i;
vis[i] = true;
dfs2(2*i);
cout << endl;
}
}
for(int i = 0; i <= t; i ++)
g[i].clear();
line.clear();
}
return 0;
}