num.4Fliptile (二进制+搜索)

wo看了大概有半个小时的题面,硬是没看懂,不知道怎么用搜索敲~ 看个题解吧,哇~ 我晕了。

题意

给定一个M*N矩阵,有些是黑色(1表示)否则白色(0表示),每翻转一个(i,j),会使得它和它周围4个格变为另一个颜色,要求翻转最少的点,使得变为全白色的矩阵,输出这个标记了翻转点的矩阵,如果有多个最优解,输出逆字典序最小的那个矩阵,若没有解,输出IMPOSSIBLE。(这个矩阵,0表示不翻转,1表示此位置要翻转)~

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
using namespace std;
int arr[30][30], flag[30][30], brr[30][30];//arr记录原图,flag记录临时的图,记录要输出的图
int M,N,dir[5][2] = { 0,0,1,0,0,1,-1,0,0,-1 };//上下左右自身五个位置
int get(int x,int y)//获得x,y点的颜色,查看颜色是否需要翻转
{
	int c=arr[x][y];
	for(int i=0;i<5;i++){
		int x1=x+dir[i][0];
		int y1=y+dir[i][1];
		c+=flag[x1][y1];
	}
	return c%2;
}
int cal()//计算2行及之后的,有解返回翻点数,无解返回-1
{
	for(int i=2;i<=M;i++)
		for(int j=1;j<=N;j++)
			if(get(i-1,j)==1)//看看上一行是否需要翻转
				flag[i][j]=1;//如果需要,就必须是下面这一行翻转来实现(因为上一行已经自身主动翻转过了)
	for(int i=1;i<=N;i++)
		if(get(M,i)) return -1;
	int ans=0;//记录反转的个数
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			ans+=flag[i][j];
	return ans;
}
int main()
{
	int min=-1;//小技巧:多组数据要更新最小值,可以在开始的时候,定义为-1
	scanf("%d%d", &M, &N);
	for(int i=1;i<=M;i++)
		for(int j=1;j<=N;j++)
			scanf("%d", &arr[i][j]);//存储原来的图
	for(int i=0;i<(1<<N);i++)//枚举第一行
	{
		memset(flag,0,sizeof(flag));
		for(int j=1;j<=N;j++)
			flag[1][j]=(i>>(j-1)) & 1;//二进制的表示方法,i右移j-1位,也就是i/(2的j-1次方)
		int num = cal();
		if(num>=0&&(min<0||min>num))
		{
			min=num;
			memcpy(brr,flag,sizeof(flag));
		}
	}
	if(min==-1) printf("IMPOSSIBLE\n");
	else
	{
		for(int i=1;i<=M;i++)
			for(int j=1;j<=N;j++)
				printf("%d%c", brr[i][j], j == N ? '\n' : ' ');
	}
    return 0;
}

思路:

大家可以这样想,一个数字要反转,可能是被动翻转(在要反转的数字的上下左右相邻的位置),也可能是主动翻转(自身翻转)我们可以先看第一行,利用四位数字的二进制,可以举出16种不同的情况,代表第一行是否要反转;判断完自身是否翻转,能够再次将其翻转的只有第二行,那么就可以接着判断(当满足第一行全部变为0时,第二行的翻转情况)了,然后一直到最后一行,如果最后一行,需要经过其自身的翻转之后,才可以全部变为0(此时上面的m-1行,都以已经确定了要不要翻转,最后一行动不了!),则不可以!如果不需要翻转,记录下来反转的个数,然后选择最小的~

P.S.

1.逆字典序输出!!!就是第一行要倒着枚举!!坑啊!
2.get到的小知识点:
在<string.h>//c++的头文件里,有一个memcpy函数,可以将一个指定字节的字符,拷贝到另一个地方~
函数原型
void memcpy(voiddest, const void *src, size_t n);
功能
由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。
3.1<<n(这是二进制的表示方法,就是将1左移n位,相当于2的n次方);
i>>n(二进制的表示方法,i右移n位,也就是i/(2的n次方)
4.&1,指的是得到这个数的二进制表示的最左边一位的结果,也就是,奇数的话,就是1,偶数的话,就是0.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值