(color{#0066ff}{题目描述})
«问题描述:
假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。
(1)每次只能在某根柱子的最上面放球。
(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。
试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。
«编程任务:
对于给定的n,计算在n根柱子上最多能放多少个球。
(color{#0066ff}{输入格式})
第1 行有1个正整数n,表示柱子数。
(color{#0066ff}{输出格式})
程序运行结束时,将n 根柱子上最多能放的球数以及相应的放置方案输出。文件的第一行是球数。接下来的n行,每行是一根柱子上的球的编号。
(color{#0066ff}{输入样例})
4
(color{#0066ff}{输出样例})
11
1 8
2 7 9
3 6 10
4 5 11
(color{#0066ff}{数据范围与提示})
有spj
4<=n<=55
(color{#0066ff}{题解})
对于每个球,拆点
本题类比于最小路径覆盖
保证路径上相连的两个点之和为完全平方数就行了
因为不知道有多少球
每次都新加入球
S向每个球的入点连 1 的边
每个球的入点向T连 1 的边
每次新加进一个球,就扫之前的球(不用管是不是最上面,因为如果不是最上面,网络流是流不过去的)
如果i能和当前的球放一起,就让i的出点向当前点的入点连 1 的边
这样跑出的最大流是最少需要多少柱子
那么我们就不断加球,每次都dinic,因为之前的图已经流完,所以复杂度不会高
如果本次流过去了一个流,说明当前的球可以放在之前的柱子里,那么就不用管,否则就要新开柱子
这样知道柱子>n,结束就行了
最后像最小路径覆盖一样直接暴力跳就行了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define _ 0
#define LL long long
inline LL in()
{
LL x=0,f=1; char ch;
while(!isdigit(ch=getchar()))(ch=='-')&&(f=-f);
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int max=105005;
const int inf=0x7fffffff;
int n,s,t;
std::queue<int> q;
struct node
{
int to,dis;
node *nxt,*rev;
node(int to=0,int dis=0,node *nxt=NULL):to(to),dis(dis),nxt(nxt){}
void *operator new (size_t)
{
static node *S=NULL,*T=NULL;
return (S==T&&(T=(S=new node[1024])+1024),S++);
}
};
typedef node* nod;
nod head[max],cur[max];
bool vis[max];
int dep[max];
inline void add(int from,int to,int dis)
{
nod t=new node(to,dis,head[from]);
head[from]=t;
}
inline void link(int from,int to,int dis)
{
add(from,to,dis);add(to,from,0);
head[from]->rev=head[to];
head[to]->rev=head[from];
}
inline bool bfs()
{
for(int i=s;i<=t;i++) dep[i]=0,cur[i]=head[i];
dep[s]=1;
q.push(s);
while(!q.empty())
{
int tp=q.front(); q.pop();
for(nod i=head[tp];i;i=i->nxt)
if(!dep[i->to]&&i->dis>0)
{
dep[i->to]=dep[tp]+1;
q.push(i->to);
}
}
return dep[t];
}
inline int dfs(int x,int change)
{
if(x==t||!change) return change;
int flow=0,ls;
for(nod i=cur[x];i;i=i->nxt)
{
cur[x]=i;
if(dep[i->to]==dep[x]+1&&(ls=dfs(i->to,std::min(change,i->dis))))
{
change-=ls;
flow+=ls;
i->dis-=ls;
i->rev->dis+=ls;
if(!change) break;
}
}
return flow;
}
inline int nxt(int x)
{
for(nod i=head[x];i;i=i->nxt)
{
if(i->to>5001&&i->to<t&&!i->dis) return i->to-5000;
}
return 0;
}
int main()
{
n=in();
int need=0,num=0;
s=0,t=10086;
while(1)
{
need++,num++,t+=2;
for(int i=1;i<num;i++)
{
int t=std::sqrt(i+num);
if(t*t!=i+num) continue;
link(i,num+5000,1);
}
link(s,num,1);
link(num+5000,t,1);
while(bfs()) need-=dfs(s,inf);
if(need>n) break;
}
printf("%d
",--num);
vis[0]=true;
for(int i=1;i<=num;i++)
if(!vis[i])
{
for(int o=i;!vis[o];o=nxt(o))
{
vis[o]=true;
printf("%d ",o);
}
putchar('
');
}
return 0;
}