OJ 7222 怀表问题__深搜

描述

怀表是由一个表链和一个表盘连接而成的。同时,表链又是由多个长度为1的表链组件连接而成的。小明现有一个表盘和多个长度为1的表链组件。他希望组合成一个完整的怀表当做生日礼物送给爸爸。

表盘和表链组件的两端都有表扣可以和其它的表链组件或者表盘相连。 一共有两种表扣设计,分别是“L” 和“V”。表链组件和表盘的左右两端可以使用相同的或者不同的表扣设计,因此我们有以下四种不同的表链组件和表盘设计方式,“LL”, “LV”, “VL”, “VV”,如“LV”表示左端的表扣设计是”L”, 右端的表扣设计是“V”。另,表盘和表链是不允许上下左右翻转的。如果把两个表链组件或者一个表链组件和表盘相连接,那么相连部分的表扣设计必须一致。此外,还必须确保表盘可以连接到表链的任意一端。

例子1:表盘是“LV”,共有5个表链组件分别是,“LL”, “LL”, “LV”, “VL”, “VL” 。现在希望组合成长度为4的怀表(表链长度是4)。 有两种正确的表链组合方式:“VLLLLVVL” 和“VLLVVLLL”。而且,对于每一种表链的组合方式,表盘都可以连接到表链的左右两端 (表盘放到表链左端使用V相连,表盘放到表链右端使用L相连)。

例子2: 表盘是“LL”,共有4个表链组件分别是,“LL”, “LV”, “VL”, “VV”。现在希望组合成长度为3的怀表(表链长度是3)。有三种正确的表链组合方式:“LLLVVL”, “LVVLLL”和“LVVVVL”。而且,对于每一种表链的组合方式,表盘都可以接到表链的任一端。

注意怀表的组合方式可能不唯一。每一种表链的组合方式都可以当做是一个由”L”和“V”

组成的字符串。如果两种表链对应的字符串是不匹配的, 那么表链也是不相同的。

输入

标准的输入包含若干组测试数据。每组数据第一行是整数N (0 < N ≤ 40)和K (0 < K ≤ N) 。
N 代表表链组件的个数,K代表要组合成的怀表长度(表链组件的个数)。 接着的N+ 1 行描述表盘(第二行)和表链组件的表扣设计。有四种可能输入: “LV”, “LL”, “VV” 和“VL”。

输出

对于每组测试数据,输出一行。 输出“YES”, 如果可能按照要求组合成怀表,并输出可能的组合方式的数目。如果不能,则输出”NO”.

样例输入

4 4
LV
LL
LV
VL
VL
4 4
VL
LL
LV
VL
VL

样例输出

YES
2
NO

提示: 本题需要使用 long long 数据类型。

分析

在网上看到一种有意思的解法,每种表链数用位数存于一个数字中,四种表链存在一个2bit的数字中,解法数目通过表扣搭配一一尝试,最终得到解,详细过程请看注释。

实现

#include <iostream>
#include <cstring>
using namespace std;
#define CODE(ch) ((ch[0] == 'L') << 1 | (ch[1] == 'L'))  //表链的编码方案。
#define LINK(a, b) ((a & 1) == (b >> 1))
int c[4], dial;  //VV, VL, LV, LL
int k;
char visited[2560001];
long long num[2560001]; //最大40 * 40 * 40 * 40 + 1
char defaultValue = 0;

//手链一头为head,表盘扣为dial时表链数目为count。
long long dfs(int code, int head, int count) {
    //如果表链能接到表盘两端则视为一种成功方案,否则返回0。
    if (k == count) return LINK(head, dial);
    if (visited[code] == defaultValue) return num[code];
    visited[code] = defaultValue;
    long long &value = num[code] = 0;
    //检测VV是否可用。
    if (code / 64000 && LINK(head, 0)) {
        value += dfs(code - 64000, 0, count + 1); //VV数目减少一就是减40 * 40 * 40
    }
    if (code / 1600 % 40 && LINK(head, 1)) {
        value += dfs(code - 1600, 1, count + 1);
    }
    if (code / 40 % 40 && LINK(head, 2)) {
        value += dfs(code - 40, 2, count + 1);
    }
    if (code % 40 && LINK(head, 3)) {
        value += dfs(code - 1, 3, count + 1);
    }
    return value;
}

int main() {
//  freopen("in.txt", "r", stdin);
    int n;
    while (~scanf("%d%d", &n, &k)) {
        char ch[3];
        defaultValue = defaultValue == 254 ? 0 : defaultValue + 1;  //省去初始化标志位时间。
        c[0] = c[1] = c[2] = c[3] = 0;
        scanf("%s", ch);
        dial = CODE(ch);
        for (int i = 0; i < n; i++) {
            scanf("%s", ch);
            c[CODE(ch)]++;
        }
        long long result = dfs(c[0] * 64000 + c[1] * 1600 + c[2] * 40 + c[3], dial, 0);
        result ? printf("YES\n%lld\n", result) : printf("NO\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值