题意
n个柱子,上面可以放球。要求相邻两个球的编号和是一个平方数。现在要将编号为1~x的所有球放在这n个柱子上。问最大的x可以是多少并输出放球的方案。
题解
就是最小路径覆盖的具体应用,两个相加为平方数的小球之间连边即可。
代码
#include<bits/stdc++.h>
#define MAXN 3000+1109
#define MAXM 4000000+1109
#define INF 0x3fffffff
using namespace std;
int n,m;
int S,T;
int dis[MAXN],dl[MAXN];
int Head[MAXN],Next[MAXM],To[MAXM],FB[MAXM],Flow[MAXM],Cnt=0;//MAXM要考虑反边
int cur[MAXN];//当前弧优化
int b[MAXN];
void ADD(int x,int y,int z)
{
Cnt++;Next[Cnt]=Head[x];Head[x]=Cnt;To[Cnt]=y;FB[Cnt]=Cnt+1,Flow[Cnt]=z;
Cnt++;Next[Cnt]=Head[y];Head[y]=Cnt;To[Cnt]=x;FB[Cnt]=Cnt-1,Flow[Cnt]=0;
}
bool BFS(int Begin,int End)
{
int t=0,w=1,x,X;
memset(dis,0xff,sizeof(dis));
dis[Begin]=0;dl[1]=Begin;
do
{
x=dl[++t];
for(int i=Head[x];i;i=Next[i])
{
X=To[i];
if(Flow[i]<=0||dis[X]>=0) continue;
dis[X]=dis[x]+1;dl[++w]=X;
}
}while(t<w);
if(dis[End]>0) return true;
else return false;
}
int Find(int x,int MFLOW,int y)
{
if(x==y) return MFLOW;
int X,h;
for(int i=cur[x];i;i=Next[i])
{
cur[x]=i;
X=To[i];
if(Flow[i]>0&&dis[x]+1==dis[X]&&(h=Find(X,min(MFLOW,Flow[i]),y)))
{
Flow[i]-=h;
Flow[FB[i]]+=h;
return h;
}
}
return 0;
}
int Solve(int x,int y)
{
int X,Ans=0;
while(1)
{
if(!BFS(x,y)) break;
for(int i=S;i<=T;i++)//初始化全体cur
cur[i]=Head[i];
while((X=Find(x,0x7fffffff,y)))
Ans+=X;
}
return Ans;
}
void print(int x,int y)
{
b[x]=true;
printf("%d ",x);
for(int i=Head[x];i;i=Next[i])
if(Flow[i]==0&&To[i]!=0)
{print(To[i]-y,y);return ;}
}
void Build(int x)
{
Cnt=0;
S=0;T=2*x+1;
Head[S]=Head[T]=0;
for(int i=1;i<=x;i++)
{
Head[i]=Head[i+x]=0;
ADD(S,i,1);ADD(i+x,T,1);
}
int X;
for(int i=1;i<x;i++)
for(int j=i+1;j<=x;j++)
{
X=sqrt(i+j);
if(X*X!=i+j) continue;
ADD(i,j+x,1);
}
}
int main()
{
scanf("%d",&n);
int l=1,r=1600,mid;
do
{
mid=(l+r)/2;
Build(mid);
if(mid-Solve(S,T)<=n) l=mid+1;
else r=mid;
}while(l!=r);
l--;
Build(l);
Solve(S,T);
printf("%d\n",l);
b[0]=true;
for(int j=1;j<=l;j++)
if(!b[j])
print(j,l),printf("\n");
return 0;
}