枚举优化
题意:
牛牛最近在玩一款叫做《膜法记录》的游戏,这个游戏的机制是这样的:
在一局游戏中,所有的敌人都排布在一个 {n}n 行 {m}m 列的网格中,牛牛指挥着他的魔法少女对敌人进行攻击。
攻击有两种类型:行blast,列blast
行blast能消灭一整行的敌人,列blast能消灭一整列的敌人
牛牛总共能够释放 {a}a 次行blast,{b}b 次列blast
给定某局游戏的初始局面,请问牛牛能否将敌人全歼?
输入描述:
第一行包含一个正整数{T}T,表示测试数据组数,接下来是{T}T组测试数据
每组测试数据的第一行有四个正整数 {n}n,{m}m,{a}a,{b}b
接下来有{n}n行,每行是一个长度为{m}m的字符串,第{i}i行第{j}j列的字符如果是*则说明这里有一个敌人,如果是.说明这里没有
输出描述:
对每组测试数据输出一行,如果能消灭所有的敌人,就输出yes,否则输出no
分析:
因为n很小,所以我们可以很容易的想到枚举我们动用魔法的行
然后在判断此刻还有多少列仍有敌人
如果仍有敌人的量小于等于b那么我们就成功可以消灭全部敌人了!
但是,困难便在于如何进行枚举。
我看了人家的题解后会了。
我们可以用一个零一串表示有哪些行被消除了,有哪些没被消除
如0101110
则第0行没消,第1行消了,第2行消了。。。。。。
既然是零一串,那么我们就可以用一个数字去存这个零一串
int长度32远大于20,所以我们可以用一个int来进行枚举。
每次改变行的选取也只需要+1就可以了十分方便。
for (int i=0;i<(1<<n);i++)
但是,然后我们要有一个剪枝,因为我们可能出现这个零一串中a的数量并没有a个,即我们并没有使用a次魔法这种时候是不用判断的
另外,当1的数量超过ashi我们也是不用判断的
我们只要判断使用了a次魔法后的情况就可以了。
接下来是判断在进行了a次魔法后,仍有敌人的列数
我们遍历每一列j
然后遍历这一列的每一行i
如果这一行没有被消除即(cur>>i)&1 == 1 cur为零一串
并且此处是敌人in[i][j] = '*'
那么这一列就仍有敌人cnt++;break。看下一列
代码如下,面向结果编程
#include<iostream> #include<algorithm> #include<string> using namespace std; const int max_n = 30; string s[max_n]; int t, n, m, a, b; int main() { ios::sync_with_stdio(0); cin >> t; while (t--) { cin >> n >> m >> a >> b; for (int i = 0;i < n;i++)cin >> s[i]; int cur = 0; for (cur = 0;cur < (1 << n);cur++) { int cnt = 0; for (int i = 0;i < n;i++) if ((cur >> i) & 1)cnt++; if (cnt != a)continue; cnt = 0; for (int i = 0;i < m;i++) { for (int j = 0;j < n;j++) { if (!((cur >> j) & 1) && s[j][i] == '*') { cnt++;break; } } } if (cnt <= b)break; } if (cur < (1 << n))cout << "yes\n"; else cout << "no\n"; } }