二进制枚举 DFS T3 ZZULIOJ 光棍节即将来临,小辣参加了某资本家的某游戏
tb.png

即四根光棍在如图场景进行游戏,每个格子有一个数值

你可以掷若干次骰子,每次随机掷出 1 ~ 6 之间的整数值,加入糖果库存

如果某次掷骰子后你的糖果库存大于某个格子的数值,且这个格子与你占领的格子相邻,那么你可以选择占领这个格子,占领后库存清 0

我们称两个格子相邻当且仅当有一条边重合

默认一开始你位于左下方(即图中显示“我方”的位置),也就是说只有左下方的格子与你相邻

小辣用二十年单身换取了 n 次掷骰子的机会,你能告诉他最好情况下最多能占领几个格子吗

输入

第一行一个整数 , 表示数据组数。对于每组数据:

第一行  个正整数,依次表示图中从左到右从上到下个格子的数值,前四个表示第一行四个格子,第五到七表示第二行三个,以此类推。

第二行  个非负整数 ,表示掷骰子的次数

, 保证所有数据 

输出

对于每组样例输出一行一个整数,表示最多能占领的格子数量

样例输入

2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
10
10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
100

样例输出

10
17

思路

刚开始的思路是直接用DFS搜索, 但有的点是需要被多次经过, 比如走到1之后得回来继续走到36时, 就不能简单地使用 st 数组标记来优化步骤。

最好的方法是用二进制枚举所有点走过的方案, 然后用DFS判断当前状态是否成立。

我们设一个长度为 17 的二进制串来记录 1~17编号的格子 是否走过。对于每一个状态 , 先判断起点是否被选中, 即 是否为1。如果选中才会从该点开始搜索。

对于不规则形状的二维坐标, 且每个点都有唯一的编号, 我们可以用一个二维数组g映射 , 同时用 来实现 的映射。

DFS中, 定义一个二进制状态 来更新当前已经走过的位置, 如果当前 , 并且用的筛子数小于筛子总数, 那说明当前的状态成立。

枚举时判断 当前位置的id值 所对应的点是否被走过, 以及是否在目标状态中, 否则就返回不走。

为了方便处理, 我们把每个点的价格转化为需要用的筛子数

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 18, M = 5;
int x[] = {1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5};
int y[] = {1, 2, 4, 5, 2, 3, 4, 2, 3, 4, 2, 3, 4, 1, 2, 4, 5};
int g[N][N];
int w[N];
int n;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int end_state, cnt, ans;
 
bool dfs(int a, int b, int state)
{
    if (end_state == state && cnt <= n)
        return true; // 可以满足
 
    int id = g[a][b]; // 找对应的价格
    if (id == -1 || !(state >> id & 1) || (end_state >> id & 1))
        return false;
 
    end_state |= 1 << id;
    cnt += w[id];
 
    for (int i = 0; i < 4; i++)
        if (dfs(a + dx[i], b + dy[i], state))
            return true;
    return false;
}
 
int main()
{
    memset(g, -1, sizeof g);
    for (int i = 0; i < 17; i++)
        g[x[i]][y[i]] = i;
 
    int T;
    cin >> T;
    while (T--)
    {
        ans = 0;
        for (int i = 0; i < 17; i++)
            cin >> w[i], w[i] = (w[i] + 5) / 6;
        cin >> n;
        for (int i = 0; i < 1 << 17; i++)
        {
 
            if (!(i >> 13 & 1))
                continue;
 
            end_state = 0, cnt = 0;
            if (dfs(x[13], y[13], i))
                ans = max(ans, (int)__builtin_popcount(i));
        }
        cout << ans << endl;
    }
    return 0;
}