文章目录
题目
PAT 1089 狼人杀-简单版(20 分)- 乙级
以下文字摘自《灵机一动·好玩的数学》:“狼人杀”游戏分为狼人、好人两大阵营。在一局“狼人杀”游戏中,1 号玩家说:“2 号是狼人”,2 号玩家说:“3 号是好人”,3 号玩家说:“4 号是狼人”,4 号玩家说:“5 号是好人”,5 号玩家说:“4 号是好人”。已知这 5 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎。扮演狼人角色的是哪两号玩家?
本题是这个问题的升级版:已知 N 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎。要求你找出扮演狼人角色的是哪几号玩家?
输入格式:
输入在第一行中给出一个正整数 N(5≤N≤100)。随后 N 行,第 i 行给出第 i 号玩家说的话(1≤i≤N),即一个玩家编号,用正号表示好人,负号表示狼人。
输出格式:
如果有解,在一行中按递增顺序输出 2 个狼人的编号,其间以空格分隔,行首尾不得有多余空格。如果解不唯一,则输出最小序列解 —— 即对于两个序列 A=a[1],…,a[M] 和 B=b[1],…,b[M],若存在 0≤k<M 使得 a[i]=b[i] (i≤k),且 a[k+1]<b[k+1],则称序列 A 小于序列 B。若无解则输出 No Solution。
输入样例 1:
5
-2
+3
-4
+5
+4
输出样例 1:
1 4
输入样例 2:
6
+6
+3
+1
-5
-2
+4
输出样例 2(解不唯一):
1 5
输入样例 3:
5
-2
-3
-4
-5
-1
输出样例 3:
No Solution
代码
#include <iostream>
#include<string>
using namespace std;
struct SF{
bool call_sf;
int call_name;
bool sf;
bool call;
};
int main(){
int n;
char ssf;
cin >> n;
getchar();
SF peo[110];
for (int i = 1; i <= n; i++){
ssf = getchar();
if (ssf == '+')peo[i].call_sf = true;
else
peo[i].call_sf = false;
cin >> peo[i].call_name;
getchar();
peo[i].sf = true;
peo[i].call = true;
}
//cout<<"*"<<endl;
//for(int i=0;i<n;i++)cout<<peo[i].call_sf <<"*"<<peo[i].call_name <<endl;
bool need = true;
for (int i = 1; i<n; i++){
for (int j = i + 1; j <= n; j++){
if (!need)break;
peo[i].sf = false;
peo[j].sf = false;
int lie = 0, liea[2];
for (int k = 1; k <= n; k++){
if (lie>2){
break;
}
if (peo[k].call_sf != peo[peo[k].call_name].sf){
peo[k].call = false;
liea[lie++] = k;
}
}
//cout <<"*"<< lie <<"*"<< endl;
if (lie == 2 &&need){
if (((peo[liea[0]].sf&&!peo[liea[1]].sf) || (!peo[liea[0]].sf&&peo[liea[1]].sf))){
need = false;
cout << i << " " << j << endl;
}
}
peo[i].sf = true;//回溯
peo[j].sf = true;
}
}
if (need)cout << "No Solution";
system("pause");
return 0;
}
思路(假设法)
1.省题
已知 N 名玩家中有 2 人扮演狼人角色,有 2 人说的不是实话,有狼人撒谎但并不是所有狼人都在撒谎。
说明N人中,为2只狼,(N-2)个好人
说谎的人有两个
两狼说的话一真一假
2.假设i和j为狼人,其他人为好人(暴搜循环所有i和j的情况)
for (int i = 1; i<n; i++){
for (int j = i + 1; j <= n; j++){
if (!need)break;
peo[i].sf = false;
peo[j].sf = false;
一开始身份都设为真,即都是好人
身份为假即为狼人
注意:每次循环完一种i和j的情况要回溯,把i和j的身份再次判为好人
设置的need是用来判断是否已经有解了,因为是按从小到大的暴搜,所以出现的第一个解就是需要输出的最小解
如果need=false那么就是不再需要循环的意思,直接出循环
3.以此为假设,判断说谎话的人数和人
for (int k = 1; k <= n; k++){
if (lie>2){
break;
}
if (peo[k].call_sf != peo[peo[k].call_name].sf){
peo[k].call = false;
liea[lie++] = k;
}
}
再次在里头进行循环,寻找说谎的人
如果他说话指向的人的身份和那个人假设的身份不同
即peo[k].call_sf != peo[peo[k].call_name].sf
那么说明那个人说谎了
说谎人数+1,并记录到liea[]数组中保留下它的序号
如果超过两人就直接不符合条件,就直接出循环
4.如果符合说谎话的人数为2再判断两狼说的话是否一真一假,如果也符合是为解
if (lie == 2&&need){
if (((peo[liea[0]].sf&&!peo[liea[1]].sf) || (!peo[liea[0]].sf&&peo[liea[1]].sf))){
need = false;
cout << i << " " << j << endl;
}
}
如果符合说谎话的人数为2
need表示还未出现解
再判断是否两狼说的话一真一假if if (((peo[liea[0]].sf&&!peo[liea[1]].sf) || (!peo[liea[0]].sf&&peo[liea[1]].sf)))
如果符合就输出,再把need设为false表示不再需要判断
5.如果循环完了仍然need说明没有符合条件的i和j,那么No Solution
if (need)cout << "No Solution";