PAT 1089 狼人杀-简单版(20 分)- 乙级

题目

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

图解过程

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Deosiree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值