这道题我弄了一下午。。。
说说解题思路吧:
(1)限定输入。先把主语和证词分离开,如果证词是“Today is XXX.”,无非7种情况,比对一下就可以了,存进结构体。如果不是,区分开证词的主语和实际证词。实际证词也只有4种情况:“am guilty.”“am not guilty.”“is guilty.”“is not guilty.”,前两种情况要求证词的主语是“I”。比对一下,也存进结构体。我用的是外链链表,方便检查是否合法。
(2)DFS哪些人说谎,把他们的证词反过来储存。我用了is这个布尔变量来记录,is=true表示这句话说的是Today is XXX或者XXX is/am guilty,is=false表示这句话说的是Today is not XXX或者XXX is/am not guilty。例如,若A的某句证词是"B is guilty."。倘若A说假话,则他的证词反过来后,就应该按照“B is not guilty.”来储存。之后把这些新的证词存到另一个数组中。依次检查这些证词,遇到前后矛盾的就回溯。为了状态判重,我用了个Trie....
(3)最后处理一下:
设cnt1为一定是罪犯的人数,cnt2为一定不是罪犯的人数,cnt3为可能是罪犯也可能不是的人数。
按顺序(注意!)检查以下几项
①如果所有情况下都出现了前后矛盾,输出Impossible。
②如果cnt2==N,输出Impossible。
③如果cnt1==1,直接输出罪犯的名字。
④如果cnt2==N-1,输出剩下那个人的名字。
⑤如果cnt3>0,或者其他情况,输出Cannot Determine.
就是这些了,编程难度不高但是很烦,尤其是(3)我搞了好久WA了几次才对。。。
RQNOJ AC
#include <cstdio>
#include <cstring>
#include <map>
#include <iostream>
using namespace std;
struct proof
{
bool is;
int from , obj;
int wd;
proof* next;
}*pr[20] , *chk[20];
map<string , int> name;
map<int , string> re_name;
short may[21] , srch[21];
int n , m , _p , p;
bool zxmd = true;
struct tr_node
{
bool flag;
int next[2];
tr_node()
{
next[0] = next[1] = flag = 0;
}
}trie[1 << 20 + 1];
int trie_size = 0;
#define ts trie_size
void ins_trie(int loc , int th)
{
if (loc == n)
{
trie[th].flag = true;
return;
}
short x = srch[loc];
if (trie[th].next[x] != 0)
ins_trie(loc + 1 , trie[th].next[x]);
else
{
trie[th].next[x] = ++ts;
ins_trie(loc + 1 , ts);
}
}
bool find_trie(int loc , int th)
{
if (loc == n)
return trie[th].flag;
short x = srch[loc];
if (trie[th].next[x] != 0)
return find_trie(loc + 1 , trie[th].next[x]);
else
return false;
}
void set_proof(int _from , int _obj , bool isornot , int weekday)
{
proof *x = new proof;
x->from = _from;
x->obj = _obj;
x->is = isornot;
x->wd = weekday;
x->next = pr[_from]->next;
pr[_from]->next = x;
}
void cpy_proof(int _from , int _obj , bool isornot , int weekday)
{
proof *x = new proof;
x->from = _from;
x->obj = _obj;
x->is = isornot;
x->wd = weekday;
x->next = chk[_from]->next;
chk[_from]->next = x;
}
bool inp(string x)
{
int i , l = x.size();
for (i = 0 ; i < l ; i++)
if (x[i] == ':')
break;
string NAME(x) , PROOF(x , i + 2);
NAME.erase(i , l - i);
int man = name[NAME];
if (man == 0) return false;
l = PROOF.size();
//Weekdays
if (PROOF == "Today is Monday.") {set_proof(man , 0 , true , 1); return true;}
if (PROOF == "Today is Tuesday.") {set_proof(man , 0 , true , 2); return true;}
if (PROOF == "Today is Wednesday.") {set_proof(man , 0 , true , 3); return true;}
if (PROOF == "Today is Thursday.") {set_proof(man , 0 , true , 4); return true;}
if (PROOF == "Today is Friday.") {set_proof(man , 0 , true , 5); return true;}
if (PROOF == "Today is Saturday.") {set_proof(man , 0 , true , 6); return true;}
if (PROOF == "Today is Sunday.") {set_proof(man , 0 , true , 7); return true;}
//Who is guilty
for (i = 0 ; i < l ; i++)
if (PROOF[i] == ' ')
break;
string OBJ(PROOF) , INST(PROOF , i + 1);
OBJ.erase(i , l - i);
if (INST == "am guilty." && OBJ == "I")
{
set_proof(man , man , true , 0);
return true;
}
if (INST == "am not guilty." && OBJ == "I")
{
set_proof(man , man , false , 0);
return true;
}
if (INST == "is guilty.")
{
set_proof(man , name[OBJ] , true , 0);
return true;
}
if (INST == "is not guilty.")
{
set_proof(man , name[OBJ] , false , 0);
return true;
}
return false;
}
short gor(short a , short b)
{
if (!a) return b;
if (!b) return a;
if ((!a) && (!b)) return 0;
if (!(a + b)) return 2;
if (a == b) return a;
}
void RES_chk()
{
for (int i = 1 ; i <= n ; i++)
{
chk[i] = new proof;
chk[i]->next = NULL;
}
}
void check()
{
short g[21] = {0} , _wd = -1;
if (find_trie(1 , 0))
return;
ins_trie(1 , 0);
RES_chk();
for (int i = 1 ; i <= n ; i++)
{
proof *P = pr[i];
P = P -> next;
while (P != NULL)
{
bool x = P->is;
if (srch[i])
x = !x;
cpy_proof(P->from , P->obj , x , P->wd);
P = P->next;
}
}
for (int i = 1 ; i <= n ; i++)
{
proof *P = chk[i];
P = P -> next;
while (P != NULL)
{
if (P->is == true && !P->wd)
{
int Obj = P->obj;
if (g[Obj] == 0)
g[Obj] = 1;
if (g[Obj] == -1)
return;
}
if (P->is == false && !P->wd)
{
int Obj = P->obj;
if (g[Obj] == 0)
g[Obj] = -1;
if (g[Obj] == 1)
return;
}
if (P->is == true && P->wd > 0)
{
if (_wd == -1)
_wd = P->wd;
if (_wd != P->wd)
return;
}
if (P->is == false && P->wd > 0)
if (P->wd == _wd)
return;
P = P->next;
}
}
for (int i = 1 ; i <= n ; i++)
may[i] = gor(may[i] , g[i]);
zxmd = false;
}
void dfs(int loc , int count)
{
if (loc == n + 1 && count < m)
return;
if (count > m)
return;
if (count == m)
{
check();
return;
}
srch[loc] = 1;
dfs(loc + 1 , count + 1);
srch[loc] = 0;
dfs(loc + 1 , count);
}
int main()
{
//freopen("logic.in" , "r" , stdin);
//freopen("logic.out" , "w" , stdout);
cin >> n >> m >> _p;
for (int i = 1 ; i <= n ; i++)
{
pr[i] = new proof;
pr[i]->next = NULL;
}
for (int i = 1 ; i <= n ; i++)
{
string x;
cin >> x;
name[x] = i;
re_name[i] = x;
}
getchar();
for (int i = 1 ; i <= _p ; i++)
{
char s[256];
cin.getline(s , 255);
string x(s);
if (inp(x))
p++;
}
dfs(1 , 0);
int guilty = 0 , cnt1 = 0 , cnt2 = 0 , cnt3 = 0;
for (int i = 1 ; i <= n ; i++)
{
if (may[i] == 1)
{
guilty = i;
cnt1++;
}
if (may[i] == -1)
cnt2++;
if (may[i] == 2)
cnt3++;
}
if (zxmd) cout << "Impossible" << endl;
else if (cnt2 == n) cout << "Impossible" << endl;
else if (cnt1 == 1) cout << re_name[guilty] << endl;
else if (cnt2 == n - 1)
{
guilty = 0;
for (int i = 1 ; i <= n ; i++)
if (may[i] == 0)
{
guilty = i;
break;
}
cout << re_name[guilty] << endl;
}
else if (cnt3 > 0) cout << "Cannot Determine" << endl;
else cout << "Cannot Determine" << endl;
return 0;
}