• 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
    ",sl);
    	for(int i=0;i<sl;i++)
    		printf("%d %d
    ",ax[i],ay[i]);
    	return 0;
    }
    
  • 相关阅读:
    INFORMATION_SCHEMA.COLUMNS表的字段信息
    如何取得一个数据表的所有列名
    CASE 函数
    Js定制窗口
    获取当前数据库中的所有用户表
    Js让状态栏不显示链接地址
    RA病人关节残障与软骨破坏而非骨破坏相关
    抗阿达木单抗的抗体可能与阿达木单抗治疗过程中静脉和动脉血栓事件相关
    长期应用阿达木单抗时所产生的抗抗体会影响疗效
    多普勒超声预测抗TNFα治疗类风湿关节炎患者的有效性:一项前瞻性队列研究
  • 原文地址:https://www.cnblogs.com/lnzwz/p/14094419.html
Copyright © 2020-2023  润新知