PAT A1139 First Contact (30分)【哈希存储边】⭐⭐⭐⭐⭐

题目描述

1139 First Contact (30分)

知识点

实现

码前思考

  1. 首先看到可能有10000个结点,肯定不能使用邻接矩阵,会爆炸的,得使用邻接表;
  2. 我一开始想的是使用DFS去判断的,结果最后一个测试用例运行超时了;
  3. 我第二个测试用例也没有过,因为居然存在-0000这种奇葩情况!!!
  4. 后来看了柳神的代码,简直不要太清晰!
  5. 柳神使用了两个数据结构,一个存储所有的边,一个存储每个结点的同性结点。 之所以存储同性结点是因为我们的题目其实就是判断同性结点是否相邻的问题!这就是能不使用DFS的原因所在,👍
  6. 对于边的编号,柳神再次使用传统艺能——x*10000+y,这是个很好用的技能,这应该是我第三次在PAT中遇到这种技巧了,下次一定要用上!
  7. 另外,对于没有排序需求时,请使用uordered_map这样时间复杂度会好很多!

代码实现

//采用两个数据结构,一个是记录所有的朋友,一个是记录所有的同性朋友
//只需要判断两者的同性朋友是否是朋友就好了
#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e4+10;

vector<int> same[maxn];
unordered_map<int,bool> friends;

struct node{
	int first;
	int second;
};

bool cmp(node a,node b){
	return a.first == b.first ? a.second < b.second : a.first < b.first;
}

int n;
int m;
int k;
vector<node> nodes;

int main(){
	scanf("%d %d",&n,&m);
	for(int i=0;i<m;i++){
		//需要进行两个步骤的筛选
		string u;
		string v;
		cin>>u>>v;
		int a = abs(stoi(u));
		int b = abs(stoi(v));
		if(u.size() == v.size()){
			//说明两者是同性
			same[a].push_back(b);
			same[b].push_back(a); 
		}
		//同时map也要进行标记
		friends[a*10000+b] =  friends[b*10000+a] = true;
	}
	
	//接下来进行读入来判断
	scanf("%d",&k);
	for(int i=0;i<k;i++){
		nodes.clear();
		int a;
		int b;
		scanf("%d %d",&a,&b);
		a = abs(a);
		b = abs(b);
		//接下来遍历所有a的同性结点和b的同性结点,来判断它们是否是朋友
		for(int i=0;i<same[a].size();i++){
			for(int j=0;j<same[b].size();j++){
				//判断两者是否是朋友
				//需要特判当前结点是否是a,b
				if(same[a][i] != b && same[b][j] != a && friends[same[a][i]*10000+same[b][j]] == true){
					node cur;
					cur.first = same[a][i];
					cur.second = same[b][j];
					nodes.push_back(cur);
				} 
			}
		}
		//进行一个排序
		sort(nodes.begin(),nodes.end(),cmp); 
		printf("%d\n",nodes.size());
		for(int i=0;i<nodes.size();i++){
			printf("%04d %04d\n",nodes[i].first,nodes[i].second);
		}
	} 
	
	return 0;
} 

码后反思

  1. 路漫漫其修远兮!
  2. 柳神判断同性的方式也很有灵性,直接通过长度判断,好棒!
  3. 使用 map 哈希存储边感觉是PAT里面常用的一种方式。我们经常是通过点来找边,但是其实仔细想想,如果直接牺牲一些空间来存储边的关系的话,我们的时间复杂度会快很多!!!

二刷代码

//注意0的情况 
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;

unordered_map<string,unordered_set<string> > mp;
int n;
int m;
int k;
string a;
string b;
vector<string> af;
vector<string> bf;

struct node{
	int c;
	int d;
}; 

vector<node> res;

vector<string> findF(string a){
	vector<string> vt;
	if(a[0] == '-'){
		for(unordered_set<string>::iterator it = mp[a].begin();it!=mp[a].end();it++){
			string tmp = *it;
			if(tmp[0] == '-'){
				vt.push_back(tmp);
			}
		}
	}else{
		for(unordered_set<string>::iterator it = mp[a].begin();it!=mp[a].end();it++){
			string tmp = *it;
			if(tmp[0] != '-'){
				vt.push_back(tmp);
			}
		}		
	}
	return vt;
}

bool cmp(node a,node b){
	return a.c==b.c ? a.d < b.d : a.c < b.c;
}

int main(){
	//freopen("input.txt", "r", stdin); 
	scanf("%d %d",&n,&m);
	string a;
	string b;
	for(int i=0;i<m;i++){
		cin>>a;
		cin>>b;
		mp[a].insert(b);
		mp[b].insert(a);
	}
	//开始进行查找
	scanf("%d",&k);
	for(int i=0;i<k;i++){
		cin>>a;
		cin>>b;
		af.clear();
		bf.clear();
		res.clear();
		//得到a的所有同性
		af = findF(a); 
		//得到b的所有同性
		bf = findF(b); 
		
		//然后寻找相同的朋友
		for(int i=0;i<af.size();i++){
			for(int j=0;j<bf.size();j++){
				string c = af[i];
				string d = bf[j];
				
				//考虑同性的情况下 
				if(c!=b && d!=a && mp[c].find(d)!=mp[c].end()){
					node tmp;
					tmp.c = abs(stoi(c));
					tmp.d = abs(stoi(d));
					res.push_back(tmp); 
				}
			}
		}
		//排序
		sort(res.begin(),res.end(),cmp);
		printf("%d\n",res.size());
		for(int i=0;i<res.size();i++){
			printf("%04d %04d\n",res[i].c,res[i].d);
		} 
	}
	return 0;	
} 
  1. 代码比较繁琐,原因还是在于没有像柳神那样,分别用两个数据结构来存储同性结点和边!
  2. 时代久远已经忘记unordered_set<string>::iterator的写法了,写成了iterator::unordered_set<string>,😓
  3. 第一次提交失败了,原因在于没有考虑下面这段代码:
    //考虑同性的情况下 
    if(c!=b && d!=a && mp[c].find(d)!=mp[c].end()){
    	node tmp;
    	tmp.c = abs(stoi(c));
    	tmp.d = abs(stoi(d));
    	res.push_back(tmp); 
    }
    
    暗恋的双方可能是同性,那么他们可能为同性朋友!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值