【D - Fliptile】

80 篇文章 0 订阅
80 篇文章 0 订阅

思路:

  • 本题看似搜索,实为枚举,关键在于题目要求输出反转串从小到大的解。
  • 第一版代码:dfs,只不过将搜索改成从右下到左上的副对角线搜索。但是肯定是不对的,因为所有格子都是相关联的(这点在下文详述),根本就不能满足题意。
  • 第二版代码,将所有搜索得到的解串存成优先队列,最后输出 Q.top()。理论上可行,但是会 TLE注意:要求出任意解和所有解只差一行代码,即
	if(ok)	return ;
  • 至此,黔驴技穷,只好搜题解,由HelloWorld10086的BLOG明白了做法。详述如下:
    1. (第一次)状态压缩,将图形存成一个 列数(N) 位的二进制数。
    2. 整个图形的翻转方式是牵一发而动全身的,形象地说:如果确定了某一行(列)翻转哪些格子,那么整个图形的反转方式也一并确定,即在这种情况下不可能出现两种方法都满足题意。
      原因:以固定第一行为例,在某种方案中,翻转了第一行该翻转的格子后,就再也不能碰第一行了。那么此时第一行的黑色格子 (mp[1][j]) 怎么办呢,只能是翻转它下面一行的同一列的格子 mp[2][j]。并且对于任意此时第一行的白色格子 (mp[1][k]),绝不允许动 (mp[2][k])。证毕。
    3. 题目要求输出最小串,就从第一行全部不翻转开始,从后边枚举。

代码:

  • 第一版:样例都过不了
#include <iostream>
#include <cstring>

using namespace std;

const int maxn = 20;

int M,N;
int K;
bool ok = false;
bool mp [maxn][maxn];
bool vis[maxn][maxn];
int moveto[4][2] = {1,0 , -1,0 , 0,1 , 0,-1};

bool check(int x,int y){
	if(x < 1 || y < 1 || x > M || y > N)
		return false;
	return true;
}

void flip(int x,int y){
	for(int k=0;k<4;k++){
		int nx = x + moveto[k][0];
		int ny = y + moveto[k][1];
		if(check(nx , ny))
			mp[nx][ny] = !mp[nx][ny];
	}
	mp [x][y] = !mp [x][y];
	vis[x][y] = !vis[x][y];
	return ;
}

bool Overall(){
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			if(mp[i][j]){
				return false;
			}
	return true;
}

void dfs(int x,int y,int deep){
	if(deep == K){
		ok = Overall();
		return ;
	}
	if(!x)
		return ;
	flip(x,y);
	if(y > 1)
		dfs(x , y - 1 , deep + 1);
	else
		dfs(x - 1 , N , deep + 1);
	if(ok)	return ;
	
	flip(x,y);
	
	if(y > 1)
		dfs(x , y - 1 , deep);
	else
		dfs(x - 1 , N , deep);
	return ;
}

int main(){
	cin>>M>>N;
	memset(vis , 0 , sizeof(vis));
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			cin>>mp[i][j];
	for(K = 0 ; K <= M*N ; K++){
		dfs(M,N,0);
		if(ok)
			break;
	}
	if(ok)
		for(int i=1;i<=M;i++)
			for(int j=1;j<=N;j++)
				if(j == N)
					cout<<vis[i][j]<<endl;
				else
					cout<<vis[i][j]<<' ' ;
	else
		cout<<"IMPOSSIBLE"<<endl;
	return 0;
}
  • 第二版:TLE
#include <iostream>
#include <cstring>
#include <string>
#include <queue>

using namespace std;

const int maxn = 20;

int M,N;
int K;
int t;
bool ok = false;
bool mp [maxn][maxn];
bool vis[maxn][maxn];
int moveto[4][2] = {1,0 , -1,0 , 0,1 , 0,-1};
priority_queue <string , vector<string> , greater<string> > Q;


bool check(int x,int y){
	if(x < 1 || y < 1 || x > M || y > N)
		return false;
	return true;
}

void flip(int x,int y){
	for(int k=0;k<4;k++){
		int nx = x + moveto[k][0];
		int ny = y + moveto[k][1];
		if(check(nx , ny))
			mp[nx][ny] = !mp[nx][ny];
	}
	mp [x][y] = !mp [x][y];
	vis[x][y] = !vis[x][y];
	return ;
}

bool Overall(){
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			if(mp[i][j]){
				return false;
			}
	return true;
}

void dfs(int x,int y,int deep){
	if(deep == K){
		string str;
		ok = Overall();
		if(ok){
			for(int i=1;i<=M;i++)
				for(int j=1;j<=N;j++)
					str += vis[i][j] + '0';
			Q.push(str);
		}
		return ;
	}
	if(!x)
		return ;
	flip(x,y);
	if(y > 1)
		dfs(x , y - 1 , deep + 1);
	else
		dfs(x - 1 , N , deep + 1);
	//if(ok)	return ;
	
	flip(x,y);
	
	if(y > 1)
		dfs(x , y - 1 , deep);
	else
		dfs(x - 1 , N , deep);
	return ;
}

int main(){
	cin>>M>>N;
	memset(vis , 0 , sizeof(vis));
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			cin>>mp[i][j];
	for(K = 0 ; K <= M*N ; K++){
		while(!Q.empty())
			Q.pop();
		dfs(M,N,0);
		if(!Q.empty())
			break;
	}
	if(!Q.empty()){
		string str = Q.top();
		for(string::iterator i = str.begin() ; i != str.end() ; i++)
			if((i - str.begin() + 1) % N == 0)
				cout<<*i<<endl;
			else
				cout<<*i<<' ' ;
	}
	else
		cout<<"IMPOSSIBLE"<<endl;
	return 0;
}
  • 第三版:391ms 668kB
//391ms		668kB


#include <iostream>
#include <cstring>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 20;

int M,N;
const int moveto[5][2] = {0,0 , 1,0 , -1,0 , 0,1 , 0,-1};
bool mp [maxn][maxn];//input 
bool rec[maxn][maxn];//output
bool vis[maxn][maxn];//temporary output
bool cur[maxn][maxn];//temporary input
int ans = INF;

void flip(int x,int y){
	vis[x][y] = true;
	for(int i=0;i<5;i++){
		int nx = x + moveto[i][0];
		int ny = y + moveto[i][1];
		cur[nx][ny] = !cur[nx][ny];//不检查边界,因为不会引起 RE 
	}
	return ;
}

bool Overall(){
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			if(cur[i][j])
				return false;
	return true;
}

void solve(int t){
	memset(vis , 0 , sizeof(vis));
	memcpy(cur , mp , sizeof(mp));
	
	int cnt = 0;
	
	for(int i=0 ; (1<<i) <= t ; i++){
		if((1<<i) & t){
			flip(1 , N-i);
			cnt++;
		}
	}
	
	for(int i=2;i<=M;i++){
		for(int j=1;j<=N;j++){
			if(cur[i-1][j]){
				flip(i , j);
				cnt++;
			}
		}
	}
	
	if(Overall() && ans > cnt){
		memcpy(rec , vis , sizeof(vis));
		ans = cnt;
	}
	return ;
}

int main(){
	cin>>M>>N;
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			cin>>mp[i][j];
	for(int t = 0 ; t < (1<<N) ; t++){
		solve(t);
	}
	if(ans == INF)
		cout<<"IMPOSSIBLE"<<endl;
	else{
		for(int i=1;i<=M;i++)
			for(int j=1;j<=N;j++)
				if(j < N)
					cout<<rec[i][j]<<' ';
				else
					cout<<rec[i][j]<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值