OpenJ_POJ C17K Lying Island

31 篇文章 0 订阅
5 篇文章 0 订阅

Problem

poj.openjudge.cn/practice/C17K?lang=en_US
vjudge.net/contest/194746#problem/K(密码:happytraining)

Meaning

有 n 个人,编号 1 ~ n,从 2 号到 n 号每人说一句话,可能是真也可能是假,话有两种:

  • Person i: Person x is a good/bad guy.,直接的命题,表示 i 号人说 x 号是好人或坏人;
  • Person i: If person x is a good/bad guy, person y is a good/bad guy.,蕴涵式,表示 i 号人说如果 x 是好/坏人,那么 y 是好/坏人。

好人一定说真话,坏人可以说真话或假话。i 号人话中出现的人都在它的编号的前 k 个人内(即 max { 1 , i - k } <= x,y < i)。问最多有多少个好人。

Analysis

因为 k 最大只有 10,考虑状压 DP。
dp[p][s]:前 p 个人,在 p 号人的前 k 个人的状态是 s 的情况下,最多有多少个好人,其中 s 的状态 0 表示坏人,1 是好人,从第 0 位到第 k-1 位分别表示第 p-1 号、p-2 号、…、第 p-k 号人的好坏状态。
判断一个人能否为好人,就看它的话与 s 中记录的是否相符,如果相符,则他可以是好人,而无论是否相符,他都可以是坏人。
第二种话是蕴含式,形如: P(x)Q(y) ,可以等价转换成:~P(x) Q(y)(! P(x) || Q(y)

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5000, K = 10;

/* 记录人的话 */
struct state
{
    int type, who[2], how[2];
    state() {}
    // 第一种话
    state(int _w, int _h): type(1)
    {
        who[0] = _w;
        how[0] = _h;
    }
    // 第二种话
    state(int _w1, int _h1, int _w2, int _h2): type(2)
    {
        who[0] = _w1, how[0] = _h1;
        who[1] = _w2, how[1] = _h2;
    }
} st[N+1];

/* 判断 p 在前 k 个人的状态是 s 时能否为好人 */
bool good_guy(int p, int s)
{
    if(p == 1) // 第 1 个人没说话,一定可以是好人
        return true;
    if(st[p].type == 1)
    {
        int w = p - st[p].who[0] - 1,
            h = s >> w & 1;
        return h == st[p].how[0];
    }
    else // type 2
    {
        int wa = p - st[p].who[0] - 1,
            wb = p - st[p].who[1] - 1,
            ha = s >> wa & 1,
            hb = s >> wb & 1;
        // ! P(x) || Q(y)
        return ha != st[p].how[0] || hb == st[p].how[1];
    }
}

int n, k, dp[N+1][1<<K];
char s[9], t[9];

int dfs(int p, int s)
{
    // 只留下低 k 位,否则 dp 会越界
    s &= (1 << k) - 1;

    if(~dp[p][s])
        return dp[p][s];

    if(p == n) // 最后一个人
        return dp[p][s] = good_guy(p, s);

    dp[p][s] = 0;

    if(good_guy(p, s)) // p 可以是好人
        dp[p][s] = 1 + dfs(p + 1, s << 1 | 1);
    // p 是坏人
    dp[p][s] = max(dp[p][s], dfs(p + 1, s << 1));

    return dp[p][s];
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &k);
        for(int i = 2, x, y; i <= n; ++i)
        {
            scanf(" Person %*d: %s", s);

            if(s[0] == 'P') // "Person" -> 第一种话
            {
                scanf("%d is a %s guy.", &x, s);
                st[i] = state(x, s[0] == 'g');
            }
            else // "If" -> 第二种话
            {
                scanf(" person %d is a %s guy, person %d is a %s guy.",
                        &x, s, &y, t);
                st[i] = state(x, s[0] == 'g', y, t[0] == 'g');
            }
        }
        memset(dp, ~0, sizeof dp);
        printf("%d\n", dfs(1, 0));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值