hdu 1882 Strange Billboard(位运算+枚举)

http://acm.hdu.edu.cn/showproblem.php?pid=1882

感觉非常不错的一道题。

给一个n*m(1<=n,m<=16)的矩阵,每一个格子都有黑白两面,当翻一个格子时,它的上下左右都要翻转,问最后使格子全变为白色的最少翻转步数。

仅仅需枚举第一行的状态即可,由于对于第i(i>=2)行j列翻转情况受上一行的制约,仅仅有当上一行也是‘X’的时候,该行j列才干翻转,使i-1行j列变为‘.’,否则i行j列不能翻转。依次进行下去,当最后一行全变为白色,说明翻转成功。

一个非常重要的优化:当n < m时,将矩阵转置,这样状态数由 (1<<m)-1 变为 (1<<n)-1。

跑了280ms,看了其它人的博客,他应该是按行翻转的。只是那一些个位运算,搞不懂。。。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <vector>
#include <queue>
#define LL long long
#define _LL __int64
using namespace std;
const int INF = 0x3f3f3f3f;

int n,m;
char map[17][17];
int sta[17],tmp[17];
int bit[17];
int ans;

void cal()
{
	bit[0] = 1;
	for(int i = 1; i < 17; i++)
		bit[i] = (bit[i-1] << 1);
}

void input()
{
	memset(sta,0,sizeof(sta)); //记录每一行的状态
	if(n >= m)
	{
		for(int i = 0; i < n; i++)
		{
			scanf("%s",map[i]);
			for(int j = 0; j < m; j++)
			{
				if(map[i][j] == ‘X‘)
					sta[i] = (sta[i] << 1) + 1;
				else sta[i] <<= 1;
			}
		}
	}
	//优化,当m < n时矩阵转换
	else
	{
		for(int i = 0; i < n; i++)
		{
			scanf("%s",map[i]);
			for(int j = 0; j < m; j++)
			{
				if(map[i][j] == ‘X‘)
					sta[j] = (sta[j] << 1) + 1;
				else sta[j] <<= 1;
			}
		}
		swap(n,m);
	}
}

void solve()
{
	int step;
	ans = INF;
	for(int i = 0; i < (1<<m); i++)
	{
		memcpy(tmp,sta,sizeof(sta));
		step = 0;
		//先找出第一行应该翻转的列并进行翻转
		for(int j = 0; j < m && step < ans; j++)
		{
			if(bit[j]&i)
			{
				step++;
				if(j > 0)
					tmp[0] ^= bit[j-1];
				if(j < m-1)
					tmp[0] ^= bit[j+1];
				tmp[0] ^= bit[j];
				tmp[1] ^= bit[j];
			}
		}
		//依据j-1行的状态依次翻转第j行
		for(int j = 1; j < n && step < ans; j++)
		{
			for(int k = 0; k < m && step < ans; k++)
			{
				if(bit[k]&tmp[j-1])
				{
					step++;
					if(k > 0)
						tmp[j] ^= bit[k-1];
					if(k < m-1)
						tmp[j] ^= bit[k+1];
					tmp[j] ^= bit[k];
					tmp[j+1] ^= bit[k];
				}
			}
		}

		if(!tmp[n-1])
			ans = min(ans,step);
	}
}

int main()
{
	cal();
	while(~scanf("%d %d",&n,&m))
	{
		if(n == 0 && m == 0) break;

		input();
		solve();

		if(ans == INF)
			printf("Damaged billboard.\n");
		else printf("You have to tap %d tiles.\n",ans);
	}
	return 0;
}

hdu 1882 Strange Billboard(位运算+枚举),布布扣,bubuko.com

时间: 2024-10-25 19:57:00

hdu 1882 Strange Billboard(位运算+枚举)的相关文章

HDU 1882 Strange Billboard(位运算)

题目链接 题意 : 给你一个矩阵,有黑有白,翻转一个块可以让上下左右都翻转过来,问最少翻转多少次能让矩阵变为全白. 思路 : 我们从第一行开始枚举要翻转的状态,最多可以枚举到2的16次方,因为你只要第一行的确定了,第二行要翻转的也就确定了,所以第一行的状态决定了最后的状态.看了网上大神,真是让位运算废了啊,,,,,太复杂了...... 1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #

HDU 1882 Strange Billboard

状态压缩,我们枚举第一行的所有状态,然后根据第一行去转变下面的行,枚举或者深搜直到最后最后一行,可以判断是不是所有的1都可以全部转换为0,记录所有的解,输出最小的一个就可以. 这里有一个很重要的优化,就是当n比m大的,转置这个矩阵,如果不加这个在G++的情况下会超时,C++900Ms多AC.代码及注释如下 #include<stdio.h> #include<iostream> #include<string.h> #include<queue> #incl

位运算+枚举

位运算http://c.biancheng.net/cpp/html/101.html 在很多情况下(比如翻棋子)在搜索时涉及大量枚举往往导致超时,位运算则很好地解决了这个问题,方便又快捷 HDU  1882   Strange Billboard http://acm.hdu.edu.cn/showproblem.php?pid=1882 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot

[hdoj]3006//位运算+枚举

题意:给出n个集合,问这些集合能组合成几种不同的集合. 思路:枚举+状态压缩.由于1<=m<=14,所以最多有2^14=16 384种状态,每种状态可以存在一个int中.枚举所有的状态,运用位运算,判断能否由这n个集合组成. #include<iostream> using namespace std; int main(){ int n, m, k, w, i, j; int ans, val[105]; while(cin >> n >> m){ for

在C#中对枚举进行位运算--枚举组合

由于枚举的基础类型类型为基本的数值类型,支持位运算,因此可以使用一个值表示多个枚举的组合,在定义枚举时需要指定枚举数为2的幂指数方便进行位运算,即枚举数为1,2,4,8-,或1,1<<1,1<<2-: public enum MyEnum { MyEnum1 = 1, //0x1 MyEnum2 = 1 << 1, //0x2 MyEnum3 = 1 << 2, //0x4 MyEnum4 = 1 << 3, //0x8 MyEnum5 = 1

HDU 6186 CS Course【前后缀位运算枚举/线段树】

[前后缀枚举] #include<cstdio> #include<string> #include<cstdlib> #include<cmath> #include<iostream> #include<cstring> #include<set> #include<queue> #include<algorithm> #include<vector> #include<map

POJ1222熄灯问题【位运算+枚举】

EXTENDED LIGHTS OUT Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 14231   Accepted: 8817 Description In an extended version of the game Lights Out, is a puzzle with 5 rows of 6 buttons each (the actual puzzle has 5 rows of 5 buttons ea

[HDU1882]--StrangeBillboard(位运算+枚举)

北莽地理形势与中原迥异致使四十万甲士水土不服加上离阳先帝对北凉徐骁忌惮已久 熬诃蛑 丹Ⅰㄗ巍 淀迢Ρ睬 篝穆诲 都尉招安进了刑房当了小头目没有挤掉谁的位置而是县尉大人大笔一挥添了一个名额 暧 卢升象微笑道:"南唐顾大祖<灰烬集>首创兵家形势论卢某本以为'兵家大言'已经言 厉霸道无匹无论是北莽战马还是披甲骑卒一剑之下只有分尸而亡的下场. ╉萌您 矣弊誄詠渭堆徒繞覓饒鄰在綱嵌譖 ラ珊棕ц 拓跋春隼嬉笑道:"雕虫小技你的驭剑杀人术比起我爹当年手下败将之一那位棋剑乐府

HDU 5491 The Next(位运算)

题意:已知D(0<=D<2^31).s1.s2,其中L为D转化为二进制数时1的个数,题目保证s1<=L<=s2,求一个数,满足以下条件: 1.比D大 2.转化为二进制时1的个数在[s1, s2]内 3.找出满足1.2条件的最小数字 分析: 1.首先将D加1,假设该数为x,求出x转化为二进制时1的个数cnt. 2.若s1<=cnt<=s2,则输出x 3.若cnt<s1,则应当增加1的数目,因为要保证找到的数字最小,所以要从二进制数的最右边开始改变. 方法:从右向左,