题目描述
«问题描述:
假设有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
【think】
刚开始的时候,我不知道该怎么处理那个球数量不一定的情况。
后来发现可以每次都跑一遍dinic,假如有超过柱子数量的小球不能够找到之前的点,就可以跳出了。
建图:
之前选择一个较大的数,对于不超过55的柱子数量,小球个数的最大。。maxn
源点:S(0)
汇点:T(2*maxn+1)
将每一个小球都要拆分成两个,也就是拆点。
然后分别与源点和汇点,建立容量为1的边。
每次增加完一个小球之后,都要跑一遍dinic,判断是否符合条件。
#include<bits/stdc++.h>
#define MAX 600000
#define INF 0x3f3f3f3f
using namespace std;
int level[MAX];
int iter[MAX];
int maxn = 72312;
int t = maxn*2+1;
struct edge
{
int to, cap, rev;
};
int next[MAX];
int vis[MAX];
vector<edge>G[MAX];
void add_edge(int from, int to, int cap)
{
struct edge e;
e.to = to;
e.cap = cap;
e.rev = G[to].size();
G[from].push_back(e);
e.to = from;
e.cap = 0;
e.rev = G[from].size()-1;
G[to].push_back(e);
}
void bfs(int s)
{
memset(level, -1, sizeof(level));
level[s] = 0;
queue<int>que;
que.push(s);
while(!que.empty())
{
int v = que.front();
que.pop();
for(int i=0; i<G[v].size(); i++)
{
edge &e = G[v][i];
if(e.cap>0&&level[e.to]<0)
{
level[e.to] = level[v]+1;
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int flow)
{
if(v==t)
return flow;
for(int &i=iter[v]; i<G[v].size(); i++)
{
edge & e = G[v][i];
if(e.cap>0&&level[e.to]>level[v])
{
int d = dfs(e.to, t, min(flow, e.cap));
if(d>0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t)
{
int flow = 0;
for(;;)
{
bfs(s);
if(level[t]<0)
return flow;
memset(iter, 0, sizeof(iter));
int f;
while((f=dfs(s, t, INF))>0)
{
flow += f;
}
}
}
int main()
{
int n;
scanf("%d", &n);//n个柱子
int num = 0, ans=0;
for(;;)
{
ans++, num++;//num代表小球的个数
add_edge(0, num, 1);
for(int i=1;i<num;i++)
{
if(sqrt(i+num)==(int)sqrt(i+num))
{
add_edge(i, num+maxn, 1);
}
}
add_edge(num+maxn, t, 1);
int f = max_flow(0, t);//每次都要跑dinic,找到边的数量
ans -= f;
if(ans>n)//如果ans现在的个数大于柱子数量,说明会有小球没有地方放。
break;
}
printf("%d\n", num-1);
memset(next, 0, sizeof(next));
for(int i=maxn;i<maxn+num;i++)
{
for(int j=0;j<G[i].size();j++)
{
edge & e= G[i][j];
if(e.cap>0)
{
next[e.to] = i-maxn;
}
}
}
memset(vis, 0, sizeof(vis));
for(int i=1;i<num;i++)
{
if(vis[i]==0)
{
vis[i] = 1;
printf("%d", i);
int a = i;
while(next[a])
{
printf(" %d", next[a]);
vis[next[a]] = 1;
a = next[a];
}
printf("\n");
}
}
return 0;
}