1. 程式人生 > 其它 >P7115-[NOIP2020]移球遊戲【構造】

P7115-[NOIP2020]移球遊戲【構造】

正題

題目連結:https://www.luogu.com.cn/problem/P7115


題目大意

\(n+1\)個柱子,前面\(n\)個上面各有\(m\)個球,球有\(n\)種顏色,每種\(m\)個。

你每次可以把一個柱子最上面的球放到另一個上面,要求在\(820000\)次內使得同種顏色的球都在同一個柱子上。

輸出方案

\(2\leq n\leq 50,2\leq m\leq 400\)


解題思路

這題好難啊,用的是洛谷題解上的做法。

首先我們列舉一種顏色\(x\),將這種顏色標記為\(1\)其他都為\(0\)

然後開始的狀態是這樣的

然後考慮先構造一個全部都是\(0\)的豎列

我們先記錄第一柱的\(1\)

的個數\(tmp\),然後把第\(n-1\)柱子的\(tmp\)個丟進第\(n+1\)柱,然後把第一柱分離到後面兩個柱子(\(1\)的放到\(n\)\(0\)的放到\(n+1\)

然後把原來的\(0\)放到第一柱,然後分離第二柱,如果是\(0\)放到第一柱否則放到第\(n+1\)柱(如果第一柱已經滿了就放進\(n+1\)柱)


然後交換一下柱子序號(用個數組存一下就好了)就變成了

然後再考慮構造全\(1\)

我們把同理把第\(1\)柱分裂到第\(n\)和第\(n+1\)柱就變成了

此時第\(n+1\)柱子上面全部是\(1\)而第\(n\)柱上面都是\(0\),然後此時我們再把剩下\(n\)

個柱子依次分離就能把所有的\(1\)提到最上面,然後把所有的\(1\)集合就好了。

最後弄出\(n-1\)個全\(0\)柱和一個全\(1\)柱我們就可以把全一柱去掉然後縮小\(n\)的值。

一直重複到\(n=2\)時我們發現我們的方法不再適用,需要特別處理。

我們按照前面的方法把第一柱分離到\(2\)\(3\)

然後把\(0\)\(1\)丟到第一個柱子,然後再把\(1\)丟進第\(3\)個柱子

然後分離第二個柱子就好了

然後這樣就能過了


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=410;
int n,m,a[N][N],cnt[N],p[N];
vector<int> aL,aR;
void mov(int x,int y){
	aL.push_back(x);
	aR.push_back(y);
	a[y][++cnt[y]]=a[x][cnt[x]--];
	return;
}
int count(int x,int y){
	int ans=0;
	for(int i=1;i<=m;i++)
		ans+=(a[x][i]==y);
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
			scanf("%d",&a[i][j]);
		cnt[i]=m;p[i]=i;
	}
	p[n+1]=n+1;
	for(int k=n;k>=3;k--){
		int tmp=count(p[1],k);
		for(int i=1;i<=tmp;i++)mov(p[k],p[k+1]);
		for(int i=1;i<=m;i++)
			if(a[p[1]][cnt[p[1]]]==k)mov(p[1],p[k]);
			else mov(p[1],p[k+1]);
		for(int i=1;i<=m-tmp;i++)mov(p[k+1],p[1]);
		for(int i=1;i<=m;i++)
			if(a[p[2]][cnt[p[2]]]==k)mov(p[2],p[k+1]);
			else if(cnt[p[1]]<m)mov(p[2],p[1]);
			else mov(p[2],p[k+1]);
		swap(p[1],p[k]);swap(p[2],p[k+1]);
		for(int i=1;i<k;i++){
			int tmp=count(p[i],k);
			for(int j=1;j<=tmp;j++)mov(p[k],p[k+1]);
			for(int j=1;j<=m;j++)
				if(a[p[i]][cnt[p[i]]]==k)mov(p[i],p[k]);
				else mov(p[i],p[k+1]);
			swap(p[i],p[k+1]);swap(p[k],p[i]);
		}
		for(int i=1;i<k;i++){
			while(a[p[i]][cnt[p[i]]]==k)mov(p[i],p[k+1]);
			while(cnt[p[i]]<m)mov(p[k],p[i]);
		}
	}
	int tmp=count(p[1],1);
	for(int i=1;i<=tmp;i++)mov(p[2],p[3]);
	for(int i=1;i<=m;i++)
		if(a[1][cnt[p[1]]]==1)mov(p[1],p[2]);
		else mov(p[1],p[3]);
	for(int i=1;i<=m-tmp;i++)mov(p[3],p[1]);
	for(int i=1;i<=tmp;i++)mov(p[2],p[1]);
	while(cnt[p[3]])mov(p[3],p[2]);
	for(int i=1;i<=tmp;i++)mov(p[1],p[3]);
	for(int i=1;i<=m;i++)
		if(a[2][cnt[p[2]]]==1)mov(p[2],p[3]);
		else mov(p[2],p[1]);
	printf("%d\n",aL.size());
	for(int i=0;i<aL.size();i++)
		printf("%d %d\n",aL[i],aR[i]);
	return 0;
}