PAT-ADVANCED1139——First Contact

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805344776077312

题目描述:

题目翻译:

1139 第一次接触

与现在不同,男孩和女孩在早年表达爱情的方式非常微妙。当男孩A迷恋女孩B时,他通常不会直接与她联系。相反,他可能会问另一个男孩C,他的一个亲密朋友,要求另一个女孩D,她是B和C的朋友,向B发送信息——这是一个很长的过程,不是吗?女孩会做类似的事情。

在这里建立了一个好友关系网络,你需要帮助一个男孩或一个女孩列出所有可能帮助他们进行第一次接触的朋友。

输入格式:

每个输入文件包含一个测试用例。对每个测试用例,第一行给出两个正整数N(1 < N <= 300)和M,分别是人的总数和友谊关系的数量。然后M行跟随,每行给出一对朋友。这里一个人用4位数ID表示。 为了表示他们的性别,我们使用负号来代表女孩。

在关系之后,给出正整数K(<= 100),这是查询的数量。然后是K行查询,每个查询给出一对恋人,以空格分隔。假设第一个ID的人迷恋上了第二个ID的人。

输出格式:

对于每个查询,首先在一行中打印他们可以找到帮助他们的不同朋友对的数量,然后在每行中打印一对朋友的ID。

如果恋人A和B是性别相反的,你必须首先打印A的性别与A相同的A的朋友,然后是B的朋友,B的朋友属于同性别。如果他们属于同一性别 那么这两个朋友必须和他们的性别相同。保证每个人只有一个性别。

朋友必须以第一个ID的非递减顺序打印,并且对于相同的第一个ID,按第二个ID的递增顺序打印。

输入样例:

10 18
-2001 1001
-2002 -2001
1004 1001
-2004 -2001
-2003 1005
1005 -2001
1001 -2003
1002 1001
1002 -2004
-2004 1001
1003 -2002
-2003 1003
1004 -2002
-2001 -2003
1001 1003
1003 -2001
1002 -2001
-2002 -2003
5
1001 -2001
-2003 1001
1005 -2001
-2002 -2004
1111 -2003

输出样例:

4
1002 2004
1003 2002
1003 2003
1004 2002
4
2001 1002
2001 1003
2002 1003
2002 1004
0
1
2003 2001
0

知识点:set集合的应用

思路:取第一个ID的同性别联系人和第二个ID的同性别联系人,判断两者之间是否有联系

本来以为是图的广度优先遍历+深度优先遍历能解决的,但是忽略了这样一个事实:一个ID被遍历之后,后序还需要被遍历到,这就使得bfs+dfs的实现异常困难,例子中的第一个查询中的1003 2003就是这样一个结果。

这里,我们把第一个ID的同性别联系人保存进set1集合中,第二个ID的同性别联系人保存进set2集合中。对于set1集合中元素的每一个ID,我们枚举其连接的另外一个顶点,如果另外一个顶点在set2中,我们就认为找到了这么一对组合,否则,就是没找到。

注意:对于set1中的元素,不能将第二个ID给放进去。同样,对于set2中的元素,不能将第一个ID给放进去

还需要注意的是,对于数字不能简单地用正负来判别男女,因为存在-0000的情况,这个时候其值是0,但是却代表女,因此我们需要用字符串来接收数据,用第一个字符是否是'-'来判断正负数,再忽略正负将其转变为数字。设立一个额外的flag数组来标记每个ID对应的男女信息。

时间复杂度和每个查询节点所连接点的数量有关。空间复杂度是O(10000)。

C++代码:

#include<iostream>
#include<vector>
#include<utility>
#include<queue>
#include<algorithm>
#include<set>

using namespace std;

bool flag[10000];	//flag[i] = true表示编号为i的人是男性
set<int> graph[10000];
int start, destination;
set<int> set1, set2;
vector<pair<int, int> > results;

int changeToInt(char* s);
void init();
bool cmp(pair<int, int> p1, pair<int, int> p2);

int main() {
	fill(flag, flag + 10000, true);
	int N, M;
	scanf("%d %d", &N, &M);
	char s1[6], s2[6];
	int id1, id2;
	for(int i = 0; i < M; i++) {
		scanf("%s %s", s1, s2);
		id1 = changeToInt(s1);
		id2 = changeToInt(s2);
		if(s1[0] == '-') {
			flag[id1] = false;
		}
		if(s2[0] == '-') {
			flag[id2] = false;
		}
		graph[id1].insert(id2);
		graph[id2].insert(id1);
	}
	int K;
	scanf("%d", &K);
	for(int i = 0; i < K; i++) {
		init();
		scanf("%s %s", s1, s2);
		start = changeToInt(s1);
		destination = changeToInt(s2);
		for(set<int>::iterator it = graph[start].begin(); it != graph[start].end(); it++) {
			if(flag[*it] == flag[start] && *it != destination) {
				set1.insert(*it);
			}
		}
		for(set<int>::iterator it = graph[destination].begin(); it != graph[destination].end(); it++) {
			if(flag[*it] == flag[destination] && *it != start) {
				set2.insert(*it);
			}
		}
		for(set<int>::iterator it1 = set1.begin(); it1 != set1.end(); it1++) {
			for(set<int>::iterator it2 = graph[*it1].begin(); it2 != graph[*it1].end(); it2++) {
				if(set2.find(*it2) != set2.end()) {
					results.push_back(make_pair(*it1, *it2));
				}
			}
		}
		sort(results.begin(), results.end(), cmp);
		printf("%d\n", results.size());
		for(int j = 0; j < results.size(); j++) {
			printf("%04d %04d\n", results[j].first, results[j].second);
		}
	}
	return 0;
}

int changeToInt(char* s) {
	int result = 0;
	if(s[0] == '-') {
		for(int i = 1; i <= 4; i++) {
			result = result * 10 + s[i] - '0';
		}
	} else {
		for(int i = 0; i <= 3; i++) {
			result = result * 10 + s[i] - '0';
		}
	}
	return result;
}

void init() {
	set1.clear();
	set2.clear();
	results.clear();
}

bool cmp(pair<int, int> p1, pair<int, int> p2) {
	if(p1.first == p2.first) {
		return p1.second < p2.second;
	} else {
		return p1.first < p2.first;
	}
}

C++解题报告:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值