专栏文章

题目标题都不会读,我太蕈了

P14155题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minol88u
此快照首次捕获于
2025/12/02 05:48
3 个月前
此快照最后确认于
2025/12/02 05:48
3 个月前
查看原文

题意

概括了一下,将就着看。
有一幅 n×mn\times m 的画,每个像素上有一个颜色,有三种操作:
  1. 选择一个 2×22\times2 的区域,将其像素顺时针旋转。
  2. 交换相邻的两个像素。
  3. 对于一个 hi×wih_i\times w_i 的区域,替换成配方列表中的另一个画面。
现在给定你初始的画和 kk 种配方,构造一个方案,在使用不大于 400400 次替换操作,且总步数在 4×1054\times10^5 步内使其变为另一幅给定的画。
n,m,k20,Σ62n,m,k\le 20,|\Sigma|\le62

思路

不拿发现旋转操作可以用 O(1)O(1) 次原地交换解决,所以先不考虑这种操作,除非上界卡得很死。
然后发现正着做操作和状态非常的多。此时考虑离线下来,时光倒流(本人昨天考试才碰到了这个 trick),从最终画面倒着考虑。
那么交换操作显然在新意义下不变。
于是替换操作就变成了每次找到一个配方列表的画面,然后使用这个配方,然后这些字符的取值我们就不关心了(不管原来是什么,总之被覆盖后就是对应的颜色),可以理解为变成了通配符 *
综上,每次枚举每个配方,判断字符数量够不够使用这个配方(注意通配符也要算进去),然后:
  • 如果通配符数量不能增加,也就是全用通配符进行的替换,那么这个操作显然无意义。
  • 否则找一个位置,把对应的字符挪过去,拼成那个配方,随后将其全部替换成通配符 *
不妨将初始局面也看成一个配方,每次判断初始局面能不能被使用。
最后别忘了把操作序列倒序输出。
然后这道题就成小模拟了,稍微用一点时间就写完了。

分析

对于我们上面的操作,分析一下是否符合题目要求的上界。
由于每次都能将至少一个字符转化成通配符,所以只会使用 O(nm)O(nm) 次配方,而把每个字符移动到我们想要的位置需要 O(n+m)O(n+m) 步,每个字符只会被尝试移动 O(k)O(k) 次。
所以只会使用 O(nm)400O(nm)\le400 次替换操作,符合题意。
所以只会使用 O(nmk(n+m))4×105O(nmk(n+m))\le4\times 10^5 次替换操作,符合题意。
所以你会发现前面提到的“找一个位置”根本不用可以特殊考虑,实际代码中直接无脑找的左上角。同时这个“旋转操作”十分没用,当作没有就行了。
由于 n,m,k,Σn,m,k,|\Sigma| 都很小,就不分析总时间复杂度了,随便实现都应该不会 T。

代码

代码难度并不高,感觉似乎没有黑?
CPP
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e2+10,maxm=4e5+10;
char c[maxn][maxn][maxn],b[maxn][maxn],(&a)[maxn][maxn]=c[0];
int h[maxm],w[maxm],vis[maxn][maxn],&n=h[0],&m=w[0],k;
struct opt{int op,x,y;}ans[maxm];int cnt[128],tot;
int check(int k){
	memset(cnt,0,sizeof cnt);
	int res=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cnt[b[i][j]]++;
	for(int i=1;i<=h[k];i++){
		for(int j=1;j<=w[k];j++){
			if(cnt[c[k][i][j]])cnt[c[k][i][j]]--,res++;
			else if(cnt['*'])cnt['*']--;
			else return -1;
		}
	}
	return res;
}
void solve(int k){
	memset(vis,0,sizeof vis);
	for(int i=1;i<=h[k];i++){
		for(int j=1;j<=w[k];j++){
			int x=0,y=0;
			for(int l=1;l<=n;l++){
				if(x||y)break;
				for(int r=1;r<=m;r++)
					if(!vis[l][r]&&b[l][r]==c[k][i][j]){x=l,y=r;break;}
			}
			for(int l=1;l<=n;l++){
				if(x||y)break;
				for(int r=1;r<=m;r++)
					if(!vis[l][r]&&b[l][r]=='*'){x=l,y=r;break;}
			}
			while(x<i)swap(b[x][y],b[x+1][y]),ans[++tot]={-3,x++,y};
			while(y<j)swap(b[x][y],b[x][y+1]),ans[++tot]={-1,x,y++};
			while(x>i)swap(b[x][y],b[x-1][y]),ans[++tot]={-4,x--,y};
			while(y>j)swap(b[x][y],b[x][y-1]),ans[++tot]={-2,x,y--};
			vis[i][j]=1;
		}
	}
	for(int i=1;i<=h[k];i++)
		for(int j=1;j<=w[k];j++)
			b[i][j]='*';
	if(k)ans[++tot]={k,1,1};
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>b[i][j];
	for(int i=1;i<=k;i++){
		cin>>h[i]>>w[i];
		for(int j=1;j<=h[i];j++)
			for(int l=1;l<=w[i];l++)
				cin>>c[i][j][l];
	}
	while(!~check(0)){
		bool f=1;
		for(int i=1;i<=k;i++)
			if(check(i)>0)f=0,solve(i);
		if(f){cout<<-1;return 0;}
	}
	solve(0);
	cout<<tot<<'\n';
	for(int i=tot;i>=1;i--){
		auto [op,x,y]=ans[i];
		cout<<op<<' '<<x<<' '<<y<<'\n';
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...