​PAT 1148​​​ ​​C++​​版

1.题意

简单版本的狼人杀游戏。所有的最终序列需满足如下关系:


  • 有且仅有两个狼人,且其中的一个狼人在说谎
  • 陈述内容不应该有冲突

2.分析

如何做这种题目?我分析了很久,想不出什么好的解法,于是采用暴力破解的方法。思路一如下:


  • step 1:使用双层for循环,对陈述内容进行处理。假设i,j两人都是说谎的人。
  • step 2:在此基础上分别假设i是狼人或者j是狼人(因为总有一个撒谎的人是狼人),这样得到了两个处理后的序列。
  • step 3:对处理后的序列判断是否是正确的序列(所谓正确的序列指的是:是否刚好有两个狼人)就ok了。
    举例如下:
    输入数据

5
-2
+3
-4
+5
+4

双层​​for​​​循环的第一次处理是:假设1,2撒谎,当1是狼人时,得到的序列是:​​+2,-3,-4,+5,+4,-1​​​【因为1是狼人,所以需要追加一个-1】;而当2是狼人时,得到的序列是:​​+2,-3,-4,+5,+4,-2​​【因为1是狼人,所以需要追加一个-2】。可以看到这两个解均不满足条件。第一个解中的狼人数超标,且存在矛盾选项;第二个解中的狼人数超标,且存在矛盾选项。


  • step 2:按照step 1的思路,继续循环。找到满足情况的所有解。
  • step 3:找到所有的解之后,按照下标进行排序,得到最终的正确解。如果没有解的话,则输出​​No Solution​​。

3.代码

include<cstdio>
#include<set>
#include<algorithm>
#include<iostream>
#define maxn 105
using namespace std;

int N;
set<int> a;

struct result{
int a;
int b;
};

result res[maxn];

//判断数组array是否是一个标准的结果
bool judge(){
//判断是否有前后矛盾的答案
for(set<int>::iterator it1=a.begin();it1!=a.end();it1++){
for(set<int>::iterator it2=a.begin();it2!=a.end();it2++){
if((*it1) == (-1) * (*it2)) return false;
}
}

int count = 0;
for(set<int>::iterator it = a.begin();it!=a.end();it++){
if(*it < 0) count++;
}
if(count != 2) return false;//说明狼人数错误
return true;
}

int index = 0;
void printRes(){
int count = 2;
int res1 , res2;//保存狼人的结果
for(set<int>::iterator it2 = a.begin();it2!=a.end();it2++){
if( (*it2) < 0 && count > 1 ) {
res[index].b = *it2 * (-1);
res1 = *it2 *(-1);
count --;
}
else if((*it2) < 0 ){
res[index].a = *it2 * (-1) ;
res2 = *it2 * (-1);
count --;
break;//处理完全了
}
}
index++;
//cout<<res2 << " "<< res1 << endl;
}

bool cmp(result r1,result r2){
if(r1.a == r2.a) return r1.b < r2.b;
return r1.a < r2.a;
}

int main(){
int i,j,k;
int info[maxn];
cin >> N;
for(i = 1;i<= N;i++){
cin >> info[i];
}

//假设撒谎的是i和 j 两个人
for(i = 1;i <= N ;i++){
for(j = i+1;j <= N;j++){
cout <<"i = "<<i<<",j = "<< j<<"\n";
a.clear();//先把所有的元素删除
//将当前的元素添加到set 中
for(k =1 ;k<=N;k++){
if(k == i || k==j) {//插入相反的元素
a.insert( ((-1)*info[k]) );
}
else {//否则 插入 info[k]
a.insert(info[k]);
}
}

//如果i是狼人
a.insert(-i);
if(judge()) printRes();

//如果j是狼人
a.erase(-i);//先将上面的 -i 删除,然后添加 -j
a.insert(-j);
if(judge()) printRes();
}
}

if(index == 0){
cout << "No Solution"<<"\n";
}else{
sort(res,res+index,cmp);
cout << res[0].a<<" "<<res[0].b;
}
}

4.测试用例

6
+6
+3
+1
-5
-2
+4
1 5


5
-2
+3
-4
+5
+4
1 4

5
-2
-3
-4
-5
-1
No Solution

5
+2
+3
+4
+5
+1
1 2


//测只有一个狼人在lie
5
-2
+3
+4
-5
-4
2 4

5
-2
+3
+4
-1
+4
1 3

5
-2
+3
+4
-1
-4
1 4

5
-2
+5
+4
-1
+4
1 5

5.执行结果

PAT 1148 C++ 版_#include

6.总结

  • 使用set保存最终的判断序列,可以去除相同的数据

7.其它

7.1主要思路

在网上看了其它的解法。其主要思想如下:





每个人说的数字保存在vector数组中;






i从1~n、j从i+1~n遍历,分别假设i和j是狼人,a数组表示该人是狼人还是好人,等于1表示是好人,等于-1表示是狼人。【这里的数组a表示真正的情况】






k从1~n分别判断k所说的话是真是假,k说的话和真实情况不同(即​​v[k] * a[abs(v[k])] < 0​​)则表示k在说谎,则将k放在lie数组中;






遍历完成后判断lie数组,如果说谎人数等于2并且这两个说谎的人一个是好人一个是狼人(即a[lie[0]] + a[lie[1]] == 0)表示满足题意,此时输出i和j并return,否则最后的时候输出No Solution~




其中较难理解的地方是:k从1~n分别判断k所说的话是真是假,k说的话和真实情况不同(即v[k] * a[abs(v[k])] < 0)则表示k在说谎,则将k放在lie数组中。这里我给出一下解释。

例如,对于如下输入:

5
-2
+3
-4
+5
+4

  • 当i=1,j=2,k =1时,a[1]=a[2]=-1。v[1] = -2,说明2是狼人;
  • ​a[abs(v[k])] = a[2]​​​,又因为​​a[2]​​表示的是:2是好人还是狼人【1是好人,-1为狼人】;
  • 所以当a[2]=1时,则​​v[k] * a[abs(v[k])] < 0​​​则表示k=1这个人在说谎,如果当a[2]=-1时,则​​v[k] * a[abs(v[k])] > 0​​则表示k=1这个人未说谎。

7.2 代码
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> v(n+1);
for (int i = 1; i <= n; i++) cin >> v[i];
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
vector<int> lie, a(n + 1, 1);
a[i] = a[j] = -1;
for (int k = 1; k <= n; k++)
if (v[k] * a[abs(v[k])] < 0) lie.push_back(k);
if (lie.size() == 2 && a[lie[0]] + a[lie[1]] == 0) {
cout << i << " " << j;
return 0;
}
}
}
cout << "No Solution";
return 0;
}
7.3 参考文章
=== update on 2019 05 07 ==

1.重要更新!!!

针对我自己的思路,即第2点中的“分析”,再次仔细分析这道题目,结合网友的博文,终于知道其坑在哪里了。

之前写的代码一直有两个测试用例过不了:

PAT 1148 C++ 版_#include_02

心里很纳闷儿,觉得没理由的啊,思路分析都是很清楚的。为什么就不能AC 呢?

在博客一文中,我找到了线索:

PAT 1148 C++ 版_i++_03

我同这个博主的想法是一样的,都是想先假设有两个人说谎了,然后再假设其中一人是狼人,这样就能找出所有的狼人了,但是事实并非如此!!因为可能存在一种情况:a是狼人,但是a却从没有在陈述中出现过,这样在上述的代码里就会漏掉这种情况,比如对于测试用例:

5
-2
-5
+4
-1
+4
1 3

其执行结果实际上应该是​​1 3​​,但是通过我的代码执行得到的就是​​No Solution​​。

PAT 1148 C++ 版_i++_04

知道问题在哪里就好办了,就是换思路,改代码呗!