枚举优化

题意:

牛牛最近在玩一款叫做《膜法记录》的游戏,这个游戏的机制是这样的:
在一局游戏中,所有的敌人都排布在一个 {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";
    }
}