codeforces1440C2 Binary Table (Hard Version)

该博客探讨了一种针对二维01矩阵的特殊操作:选择2×2子矩阵,翻转其中三个不同位置的元素,目标是将所有元素变为0。博主通过预处理最短操作序列并按行处理矩阵,展示了如何在操作次数不超过nm的情况下完成任务。当n大于m时,博主建议转置矩阵后再处理。最后,博主提供了算法实现和复杂度分析。
摘要由CSDN通过智能技术生成
题目:

给定一个 n × m n \times m n×m的01矩阵,现有一种操作:选择一个 2 × 2 2 \times 2 2×2的子矩阵,选择其中三个不同位置的元素,将它们翻转(0变成1,1变成0)。使用这种操作,将01矩阵的元素全变成0。让你构造操作序列,使操作总数小于等于 n m nm nm
( 2 ≤ n , m ≤ 100 ) (2 \le n,m \le 100) (2n,m100)

题解:

首先可以将每个状态的 2 × 2 2 \times 2 2×2的矩阵清零的最短长度的操作序列进行预处理,一共也就 2 4 2^4 24种状态,建个转移图,反向建边,从全0的状态出发 b f s bfs bfs跑最短路即可。
接下来考虑构造,我们两行两行地来看,对于 i i i行和 i + 1 i+1 i+1行,我们将 j j j列( j ≤ m − 2 j \le m-2 jm2)和 j + 1 j+1 j+1列的四个元素组成 2 × 2 2 \times 2 2×2的矩阵,进行操作将 j j j列的两个元素变成0,对于最后两列组成的 2 × 2 2 \times 2 2×2矩阵,直接用之前预处理好的对应状态的操作序列即可。如果 n n n为奇数,那么最后一行就多出来了,那么我们将 n − 1 n-1 n1行和 n n n行再进行一次上述操作。
来看一下这种操作方式的次数,上界为

l i m = ⌈ n 2 ⌉ ( m − 2 ) + 4 ⌊ n 2 ⌋ + 3 lim=\lceil \tfrac{n}{2} \rceil(m-2)+4 \lfloor \tfrac{n}{2} \rfloor +3 lim=2n(m2)+42n+3

≤ ⌈ n 2 ⌉ ( m − 2 ) + 4 ⌈ n 2 ⌉ − 1 \le \lceil \tfrac{n}{2} \rceil(m-2)+4 \lceil \tfrac{n}{2} \rceil -1 2n(m2)+42n1

= ⌈ n 2 ⌉ ( m + 2 ) − 1 =\lceil \tfrac{n}{2} \rceil(m+2)-1 =2n(m+2)1

= ⌈ n 2 ⌉ m + 2 ⌈ n 2 ⌉ − 1 = \lceil \tfrac{n}{2} \rceil m+2\lceil \tfrac{n}{2} \rceil -1 =2nm+22n1

≤ ⌈ n 2 ⌉ m + n + 1 − 1 \le \lceil \tfrac{n}{2} \rceil m+n+1 -1 2nm+n+11

= ⌈ n 2 ⌉ m + n =\lceil \tfrac{n}{2} \rceil m+n =2nm+n

≤ ( n − 1 ) m + n \le (n-1)m+n (n1)m+n

n ≤ m n \le m nm时,

l i m ≤ ( n − 1 ) m + m = n m lim \le (n-1)m+m=nm lim(n1)m+m=nm

所以可以满足题目的要求。当 n > m n > m n>m时,我们就将整个矩阵转置后再进行处理,最后输出答案时不要忘记要将行列反着输出。

复杂度: O ( n m ) O(nm) O(nm)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=105;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int t,n,m,lim;
int a[maxn][maxn];
char s[maxn];
struct edge{
	int v,nop;
};
vector<edge>g[20];
vector<int>op[20];
queue<int>q;
int vis[20];
vector<int>pre[20];
vector<vector<pii> >ans;
int b[maxn][maxn];
void build(){
	lim=4;
	for(int i=1;i<(1<<lim);i++){
		int base=(1<<lim)-1;
		for(int j=0;j<4;j++){
			int tmp=base^(1<<j);
			int v=i^tmp;
			g[v].pb((edge){i,j});
		}
	}
}
void bfs(){
	q.push(0);
	vis[0]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(auto e:g[u]){
			if(vis[e.v])continue;
			vis[e.v]=1;
			q.push(e.v);
			pre[e.v].assign(pre[u].begin(),pre[u].end());
			pre[e.v].pb(e.nop);
		}
	}
}
void print(vector<pii>ve,int nop){
	vector<pii>v;
	for(int i=0;i<4;i++){
		if(i!=nop){
			v.pb(ve[i]);
		}
	}
	ans.pb(v);
}
void init(){
	build();
	bfs();
	for(int i=1;i<(1<<lim);i++){
		reverse(pre[i].begin(),pre[i].end());
	}
}
int trans(vector<pii>ve){
	int sta=0;
	for(int i=0;i<sz(ve);i++){
		if(a[ve[i].fi][ve[i].se]){
			sta|=(1<<i);
		}
	}
	return sta;
}
void solve(vector<pii>ve){
	int sta=trans(ve);
	for(int i=0;i<sz(pre[sta]);i++){
		print(ve,pre[sta][i]);
	}
	for(int i=0;i<sz(ve);i++){
		a[ve[i].fi][ve[i].se]=0;
	}
}
int main(void){
	// freopen("in.txt","r",stdin);
	init();
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			scanf("%s",s+1);
			for(int j=1;j<=m;j++){
				a[i][j]=s[j]-'0';
			}
		}
		int rev=0;
		if(n>m){
			rev=1;
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					b[i][j]=a[i][j];
				}
			}
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					a[j][i]=b[i][j];
				}
			}
			swap(n,m);
		}
		vector<pii>ve,v;
		ans.clear();
		for(int i=1;i<=n;i+=2){
			if(i==n)i--;
			for(int j=1;j<=m-2;j++){
				int cnt=a[i][j]+a[i+1][j];
				if(cnt==0)continue;
				if(cnt==1){
					if(a[i][j]){
						a[i][j]=0;
						a[i][j+1]^=1;
						a[i+1][j+1]^=1;
						v.clear();
						v.pb(mp(i,j));
						v.pb(mp(i,j+1));
						v.pb(mp(i+1,j+1));
						ans.pb(v);
					}
					else{
						a[i+1][j]=0;
						a[i][j+1]^=1;
						a[i+1][j+1]^=1;
						v.clear();
						v.pb(mp(i+1,j));
						v.pb(mp(i,j+1));
						v.pb(mp(i+1,j+1));
						ans.pb(v);
					}
				}
				else{
					a[i][j]=a[i+1][j]=0;
					a[i][j+1]^=1;
					v.clear();
					v.pb(mp(i,j));
					v.pb(mp(i+1,j));
					v.pb(mp(i,j+1));
					ans.pb(v);
				}
			}
			ve.clear();
			ve.pb(mp(i,m-1));
			ve.pb(mp(i,m));
			ve.pb(mp(i+1,m-1));
			ve.pb(mp(i+1,m));
			solve(ve);
		}
		printf("%d\n",sz(ans));
		for(int i=0;i<sz(ans);i++){
			for(int j=0;j<sz(ans[i]);j++){
				if(!rev)
					printf("%d %d ",ans[i][j].fi,ans[i][j].se);
				else{
					printf("%d %d ",ans[i][j].se,ans[i][j].fi);
				}
			}
			puts("");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值