P2765 魔术球问题

\(\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\n",--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('\n');
		}
	return 0;
}
posted @ 2018-12-12 14:52  olinr  阅读(138)  评论(0编辑  收藏  举报