P7115 移球游戏

https://www.luogu.com.cn/problem/P7115

考场上做出了70分做法

先考虑能把一个柱子上的球做什么样的操作,有一种想法是把某一种颜色的球全部放到顶部
具体做法大概是这样,先选择任意一个满栈 (O),和一个空栈 (E),操作:

  • 记这个要操作的栈为 (X),设 (X) 中有 (num) 个这种颜色的球
  • (H) 中最上面的 (num) 个放入 (E)
  • 对于 (X),把所有是这种颜色的球放入 (O) 中,不是的放入 (E) 中。此时 (X) 空,(E,O) 均已满
  • (E) 中最上面的 (m-num) 个放回 (X) 中,再把 (O) 中最上面的 (num) 个放回 (X)。此时对于 (X) 的操作已完成
  • (E) 中剩下的 (num) 个放回 (O),此时 (O) 已经恢复原状,(E)

做这样一次操作的操作次数是 (2m+2num)

由此,一种比较显然的做法就是,枚举每种颜色,把每个混乱(就是不是单色)的柱子中的这种颜色都放到顶部,然后再把他们全都提取到空栈中,就又多了一个不混乱的柱子
然后继续枚举下一个颜色,直到所有柱子都不混乱
在卡一卡操作次数的常数,计算的可以过 70 分(实际上ccf数据过了80分)

对于正解,采用分治的方法
对于颜色区间 ([l,r]),把所有 ([l,mid]) 的颜色视为同一种颜色,([mid+1,r]) 也视为同一种
然后把 ([l,mid]) 的提升到对应柱子的顶部,然后对于每两个柱子,我们总是能令其中的一个中的所有颜色都变成 ([l,mid])([mid+1,r])
把每个柱子都选择另一个柱子来这样做就可以了
这样,有 (mid-l+1) 个柱子变成了颜色 ([l,mid]),另外 (r-mid) 个变成了 ([mid+1,r]),再进行递归即可
(l=r) 时,也就完成了操作,当前柱子上的颜色统一了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define reg register
inline int read(){
	register int x=0;register int y=1;
	register char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
	return y?x:-x;
}
int n,m;
int stack[55][455],top[55];
int A[820006],B[820006],k;
inline void putt(int a,int b){
//		printf("%d %d
",a,b);
	A[++k]=a;B[k]=b;
	stack[b][++top[b]]=stack[a][top[a]];
	top[a]--;
}
inline int cnt(int i,int p){
	int num=0;
	for(reg int o=1;o<=m;o++) num+=(stack[i][o]<=p);
	return num;
}
void work(int l,int r,int col[]){
	if(l==r) return;
	int mid=(l+r)>>1;
	for(reg int i=1;i<=r-l+1;i++){
		int other=i+1;
		if(other==r-l+2) other=1;
		int backup=i;
		i=col[i];other=col[other];
		int num=cnt(i,mid),dibu;
		if(!num) continue;
		for(reg int o=1;o<=num;o++) putt(other,n+1);
		for(reg int o=1;o<=m;o++){
			if(stack[i][o]<=mid){dibu=o;break;}
		}
		for(reg int o=m;o>=dibu;o--){
			if(stack[i][o]<=mid) putt(i,other);
			else putt(i,n+1);
		}
		for(reg int o=dibu;o<=m-num;o++) putt(n+1,i);
		for(reg int o=m-num+1;o<=m;o++) putt(other,i);
		for(reg int o=m-num+1;o<=m;o++) putt(n+1,other);
		i=backup;
	}//将所有 [l,mid] 颜色置于每个柱子的顶部
	int col1[52],col2[52];
	col1[0]=col2[0]=0;
	for(reg int i=1;i<=r-l;i++){
		int backup=i;
		int i1=col[i+1];i=col[i];
		int num1=cnt(i,mid),num2=cnt(i1,mid);
		if(num1+num2>=m){
			col1[++col1[0]]=i;
			for(reg int o=m;o;o--) putt(i,n+1);
			for(reg int o=m;stack[i1][o]<=mid&&o;o--) putt(i1,i);
			for(;top[i1]<m;) putt(n+1,i1);
			for(;top[i]<m;) putt(n+1,i);
		}
		else{
			col2[++col2[0]]=i;
			for(reg int o=m;stack[i][o]<=mid&&o;o--) putt(i,n+1);
			for(reg int o=m;stack[i1][o]<=mid&&o;o--) putt(i1,n+1);
			for(;top[i]<m;) putt(i1,i);
			for(;top[i1]<m;) putt(n+1,i1);
		}
		i=backup;
	}
	if(stack[col[r-l+1]][1]<=mid) col1[++col1[0]]=col[r-l+1];
	else col2[++col2[0]]=col[r-l+1];
	work(l,mid,col1);work(mid+1,r,col2);
}
int main(){
	n=read();m=read();
	for(reg int i=1;i<=n;i++){
		for(reg int j=1;j<=m;j++) stack[i][j]=read();
		top[i]=m;
	}
	int col[52];
	for(reg int i=1;i<=n;i++) col[i]=i;
	work(1,n,col);
	printf("%d
",k);
	for(reg int i=1;i<=k;i++) printf("%d %d
",A[i],B[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/14106351.html