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;
}