NOIP2020移球游戏
题意:
有\(n+1\)个柱子,每个柱子上最多放m个盘。
起初,第\(n+1\)个柱子是空的,其余的柱子都放满了盘。盘有n种颜色,每种各m个。
要求移动,使颜色归位。
首先,考虑\(n=2\)的做法。即只有黑白两种颜色。
可以这样做:
先将所有黑色移到白色上面,然后就显然了。
设1号柱子有x个黑色的。
那么,将2号柱子最上面的x个盘移至3号,使2号柱子空出x个位置。
然后,对于1号柱子的所有盘,黑色的移到2号,白色的移到3号。
然后,把3号柱子上面的那些白色盘移到1号,再把2号柱子上面的那些黑色盘移到1号。
最后,把x个盘移回2号。
这样,在保证2号柱子不动的前提下,整理了1号。
对于\(n>2\)的情况:
分治。
把一半颜色染成黑色,另一半染成白色。
按照上述做法整理所有柱子。
每次,挑选两根柱子,利用空柱子,便可以整理出一根颜色只有黑或白的柱子。
这样,把所有柱子都变成单色的(黑或白),就可以继续分治了。
复杂度(操作次数):\(O(nmlogn)\)。
代码:
#include <stdio.h>
int rs[402],M;
struct Stk
{
int st[402],tp;
Stk(){tp=0;}
void push(int x)
{
st[tp++]=x;
}
int top()
{
return st[tp-1];
}
int pop()
{
return st[--tp];
}
int count()
{
int rt=0;
for(int i=0;i<tp;i++)
rt+=rs[st[i]];
return rt;
}
/*void print()
{
for(int i=0;i<tp;i++)
printf("%d ",st[i]);
}*/
};
Stk sz[52];
int ax[820010],ay[820010],sl=0,em,ss[52];
void move(int x,int y)
{
ax[sl]=x;ay[sl++]=y;
sz[y].push(sz[x].pop());
}
void up1(int b,int c)
{
int a=em,s=ss[b];
for(int i=0;i<s;i++)
move(c,a);
while(sz[b].tp)
{
if(rs[sz[b].top()])
move(b,c);
else
move(b,a);
}
for(int i=0;i<M-s;i++)
move(a,b);
for(int i=0;i<s;i++)
move(c,b);
for(int i=0;i<s;i++)
move(a,c);
}
void rev(int &a)
{
ss[em]=M-ss[a];
for(int i=0;i<M;i++)
move(a,em);
int t=em;
em=a;a=t;
}
void tog(int &a,int &b)
{
int fn[2]={0};
int sa=ss[a],sb=ss[b];
if(rs[sz[b].top()]!=rs[sz[a].top()])
fn[1]=1,sb=M-sb;
if(sa+sb>M)
fn[0]^=1,fn[1]^=1;
if(fn[0])rev(a);
if(fn[1])rev(b);
if(ss[a]>ss[b])
{
int t=a;
a=b;b=t;
}
for(int i=0;i<ss[a];i++)
move(a,em);
for(int i=0;i<ss[b];i++)
move(b,em);
for(int i=0;i<ss[a];i++)
move(b,a);
for(int i=0;i<ss[a]+ss[b];i++)
move(em,b);
ss[b]+=ss[a];
}
void dfs(int zz[52],int co[52],int n)
{
if(n<=1)
return;
int m=n/2;
for(int i=0;i<m;i++)rs[co[i]]=1;
for(int i=m;i<n;i++)rs[co[i]]=0;
for(int i=0;i<n;i++)ss[zz[i]]=sz[zz[i]].count();
for(int i=0;i<n;i++)up1(zz[i],zz[(i+1)%n]);
int zl[52],a=0,zr[52],b=0;
for(int i=0;i<n-1;i++)
tog(zz[i],zz[i+1]);
for(int i=0;i<n;i++)
{
int t=zz[i];
if(rs[sz[t].top()])
zl[a++]=t;
else
zr[b++]=t;
}
dfs(zl,co,m);
dfs(zr,co+m,n-m);
}
int main()
{
//freopen("ball.in","r",stdin);
//freopen("ball.out","w",stdout);
int n;
scanf("%d%d",&n,&M);
for(int i=1;i<=n;i++)
{
for(int j=0;j<M;j++)
{
int a;
scanf("%d",&a);
sz[i].push(a);
}
}
em=n+1;
int zz[52],co[52];
for(int i=0;i<n;i++)
zz[i]=co[i]=i+1;
dfs(zz,co,n);
printf("%d\n",sl);
for(int i=0;i<sl;i++)
printf("%d %d\n",ax[i],ay[i]);
return 0;
}