• 【洛谷P7115】移球游戏


    题目

    题目链接:https://www.luogu.com.cn/problem/P7115
    小 C 正在玩一个移球游戏,他面前有 (n + 1) 根柱子,柱子从 (1 sim n + 1) 编号,其中 (1) 号柱子、(2) 号柱子、……、(n) 号柱子上各有 (m) 个球,它们自底向上放置在柱子上,(n + 1) 号柱子上初始时没有球。这 (n imes m) 个球共有 (n) 种颜色,每种颜色的球各 (m) 个。

    初始时一根柱子上的球可能是五颜六色的,而小 C 的任务是将所有同种颜色的球移到同一根柱子上,这是唯一的目标,而每种颜色的球最后放置在哪根柱子则没有限制。

    小 C 可以通过若干次操作完成这个目标,一次操作能将一个球从一根柱子移到另一根柱子上。更具体地,将 (x) 号柱子上的球移动到 (y) 号柱子上的要求为:

    1. (x) 号柱子上至少有一个球;
    2. (y) 号柱子上至多有 (m - 1) 个球;
    3. 只能将 (x) 号柱子最上方的球移到 (y) 号柱子的最上方。

    小 C 的目标并不难完成,因此他决定给自己加加难度:在完成目标的基础上,使用的操作次数不能超过 (820000)。换句话说,小 C 需要使用至多 (820000) 次操作完成目标。

    小 C 被难住了,但他相信难不倒你,请你给出一个操作方案完成小 C 的目标。合法的方案可能有多种,你只需要给出任意一种,题目保证一定存在一个合法方案。

    思路

    假设我们已经处理好了 (1sim i-1) 的小球,现在要把颜色为 (i) 的小球放在第 (i) 个柱子里。设颜色 (i) 的球为 (1),其余颜色的球为 (0)
    先把第 (i) 个柱子内的球全部变为 (0)。具体的,我们先把第 (i+1) 个柱子内的所有球全部移出去,如果柱子 (i) 内有 (k)(1) 球,那么我们就把柱子 (i+2) 的前 (k) 个球放到第 (i+1) 个柱子内。然后依次弹出柱子 (i) 的小球,如果顶端的球是 (1),那么球放到柱子 (i+2) 上,否则放在柱子 (i+1) 上。
    这样我们就把柱子 (i) 清空,并且把原来里面的球是 (1) 的放到了柱子 (i+2) 上,其余放到了 (i+1) 上。
    接下来把 (i+1) 顶部的所有 (0) 球放到柱子 (i) 上,然后对柱子 (i+3) 进行操作:

    • 如果柱子 (i+3) 顶部是 (0) 球并且柱子 (i) 没满,那么就把柱子 (i+3) 顶部的球放到 (i) 上。
    • 否则把柱子 (i+3) 的球放到 (i+1) 上。

    那么此时柱子 (i) 里面就全是 (0) 球了,而且因为 (1) 球总数只有 (m) 个,所以不用担心柱子会超过 (m) 个球。
    接下来重复此操作:

    • 枚举 (j=i+1 o n),将 (j) 柱子变空。由于之前操作完成后一定有一个柱子是空的,所以我们没有必要把球一个一个弹出,直接设 (id[x]) 表示 (x) 位置的柱子实际上是哪一个,然后交换编号即可。
    • 此时 (i) 柱子是全 (0) 的,(j) 柱子是空的,设柱子 (j+1)(k)(1) 球,那么我们把柱子 (i) 的顶部 (k) 个球扔到柱子 (j) 中,然后用类似的方法,将柱子 (j+1) 中的 (1) 球扔进 (i) 的顶端,(0) 球扔进 (j) 的顶端。
    • 经过上面两补后,我们发现 (i) 柱子顶端是若干个 (1) 球,(j) 柱子变为了全 (0)(j+1) 柱子变空了!
    • 那么交换柱子之间的编号,使得 (i) 柱子全 (0)(j+1) 柱子为空,继续循环。

    结束重复后,我们发现所有 (1) 球到了柱子 (1sim n) 的顶端,柱子 (n+1) 为空,那么我们就直接把所有柱子顶端的 (1) 球扔到柱子 (n+1) 中即可。
    这样我们就成功的用 (4) 根柱子之间的转换,让 (1) 球全部到了柱子 (n+1)。接下来交换 (id[n+1])(id[i]),继续做颜色为 (i+1) 的球即可。
    需要注意的是,当我们剩余柱子不够 (4) 个时,我们无法再这样操作。此时只剩下 (3) 个柱子和两种颜色的球没有分类,那么随便搞一下就好了,具体方法很多,不再赘述。
    关于操作次数,对于第 (i) 个球,操作次数最多为 (6m+(n-i)m),再加上最后只剩两种球的时候的操作次数,总操作次数为 (6nm+frac{(n+1)(n-2)}{2}m+8m=612800)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=55,M=410,MAXN=820010;
    int n,m,tot,id[N],cnt[N],col[N][M],ans[MAXN][2];
    
    void move(int x,int y)
    {
    	if (x==y) return;
    	ans[++tot][0]=x; ans[tot][1]=y;
    	col[y][++cnt[y]]=col[x][cnt[x]];
    	col[x][cnt[x]]=0; cnt[x]--;
    }
    
    int main()
    {
    	freopen("data.txt","r",stdin);
    	freopen("ans.txt","w",stdout);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		cnt[i]=m; id[i]=i;
    		for (int j=1;j<=m;j++)
    			scanf("%d",&col[i][j]);
    	}
    	id[n+1]=n+1;
    	for (int i=1;i<=n-2;i++)
    	{
    		for (int j=1;j<=n+1;j++)
    			while (j!=i+1 && cnt[id[j]]<m && cnt[id[i+1]]) move(id[i+1],id[j]);
    		for (int j=cnt[id[i]];j>=1;j--)
    			if (col[id[i]][j]==i) move(id[i+2],id[i+1]);
    		for (int j=cnt[id[i]];j>=1;j--)
    			if (col[id[i]][j]==i) move(id[i],id[i+2]);
    				else move(id[i],id[i+1]);
    		for (int j=cnt[id[i+1]];j>=1 && col[id[i+1]][j]!=i;j--)
    			move(id[i+1],id[i]);
    		for (int j=cnt[id[i+3]];j>=1;j--)
    			if (col[id[i+3]][j]==i || cnt[id[i]]==m) move(id[i+3],id[i+1]);
    				else move(id[i+3],id[i]);
    		for (int j=i+1;j<=n;j++)
    		{
    			for (int k=1;k<=n+1;k++)
    				if (!cnt[id[k]])
    				{
    					swap(id[k],id[j]);
    					break;
    				}
    			for (int k=cnt[id[j+1]];k>=1;k--)
    				if (col[id[j+1]][k]==i) move(id[i],id[j]);
    			for (int k=cnt[id[j+1]];k>=1;k--)
    				if (col[id[j+1]][k]==i) move(id[j+1],id[i]);
    					else move(id[j+1],id[j]);
    			swap(id[i],id[j]);
    		}
    		for (int j=i+1;j<=n+1;j++)
    			for (int k=cnt[id[j]];k>=1 && col[id[j]][k]==i;k--)
    				move(id[j],id[n+1]);
    		swap(id[n+1],id[i]);
    	}
    	for (int i=cnt[id[n+1]];i>=1;i--)
    		if (cnt[id[n-1]]<m) move(id[n+1],id[n-1]);
    			else move(id[n+1],id[n]);
    	for (int i=cnt[id[n-1]];i>=1;i--)
    		if (col[id[n-1]][i]==n) move(id[n],id[n+1]);
    	for (int i=cnt[id[n-1]];i>=1;i--)
    		if (col[id[n-1]][i]==n) move(id[n-1],id[n]);
    			else move(id[n-1],id[n+1]);
    	for (int i=cnt[id[n]];col[id[n]][i]==n;i--)
    		move(id[n],id[n-1]);
    	for (int i=cnt[id[n+1]];col[id[n+1]][i]!=n && cnt[id[n-1]]<m;i--)
    		move(id[n+1],id[n-1]);
    	for (int i=cnt[id[n+1]];i>=1;i--)
    		move(id[n+1],id[n]);
    	for (int i=cnt[id[n-1]];col[id[n-1]][i]!=n;i--)
    		move(id[n-1],id[n+1]);
    	for (int i=cnt[id[n]];i>=1;i--)
    		if (col[id[n]][i]==n) move(id[n],id[n-1]);
    			else move(id[n],id[n+1]);
    	printf("%d
    ",tot);
    	for (int i=1;i<=tot;i++)
    		printf("%d %d
    ",ans[i][0],ans[i][1]);
    	return 0;
    }
    
  • 相关阅读:
    国内外手机号码正则表达式
    apt安装Neo4j
    经典决策树模型
    自动文档摘要评价方法
    scrapy-splash解析javascript
    ubuntu安装splash
    iptables的删除命令中的相关问题
    ARTS第七周
    ARTS第六周
    ARTS第五周
  • 原文地址:https://www.cnblogs.com/stoorz/p/14124145.html
Copyright © 2020-2023  润新知