zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    开机自启动程序 注册表
    windows系统调用 线程创建
    windows系统调用 进程终止
    windows系统调用 进程快照
    线性表基本维护[ACM]
    获取操作系统版本号
    获得进程句柄
    <五>JDBC_利用反射及JDBC元数据编写通用的查询方法
    <四>JDBC_PreparedStatement的使用
    <三>JDBC_面向对象思想的体现
  • 原文地址:https://www.cnblogs.com/lnzwz/p/14094419.html
Copyright © 2011-2022 走看看