PAT.1139 First Contact
a如果喜欢b,需要找一个自己的同性朋友c去联系一个b的同性朋友d,同时要求cd为朋友,给定图和若干查询,求每个查询下所有可能的{c,d}。
基于图的题目一开始想到的还是那老几样————各种搜索和最短路算法,显然这道题就是要求顶点ab之间的一条长度为3的路径,同时对cd两顶点的性别作出要求。
一些尝试
按照上面的思路,用dfs写了一手,测试点2答案错误的同时测试点5超时。其中测试点2的问题应该是关于id为0000的情况,因为用整形保存性别信息的话-0和0都是0。
以下代码得分24/30。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int peopleCnt,relationCnt,queryCnt,tId1,tId2,gender1,gender2,vis[20005];
vector<int> neighbor[20005];
vector<int> pathForSearch;
vector<pair<int,int> > res;
void dfs(int cVertex){
int len = pathForSearch.size();
if(len == 4 && pathForSearch.back() == tId2){
res.push_back({pathForSearch[1],pathForSearch[2]});
return;
}else if(pathForSearch.back() == tId2) return;
for(int nextVertexId : neighbor[cVertex]){
int cGender = (nextVertexId <= 9999 ? 1 : 0);
if(len == 1 && cGender != gender1) continue;
if(len >= 2 && cGender != gender2) continue;
if(vis[nextVertexId] == 1) continue;
vis[nextVertexId] = 1;
pathForSearch.push_back(nextVertexId);
dfs(nextVertexId);
pathForSearch.pop_back();
vis[nextVertexId] = 0;
}
}
int main(){
cin>>peopleCnt>>relationCnt;
for(int i = 0 ; i < relationCnt ; ++i){
cin>>tId1>>tId2;
tId1 += 10000;
tId2 += 10000;
neighbor[tId1].push_back(tId2);
neighbor[tId2].push_back(tId1);
}
cin>>queryCnt;
for(int i = 0 ; i < queryCnt ; ++i){
res.clear();
memset(vis,0,sizeof(vis));
cin>>tId1>>tId2;
tId1 += 10000;
tId2 += 10000;
gender1 = (tId1 <= 9999 ? 1 : 0);
gender2 = (tId2 <= 9999 ? 1 : 0);
vis[tId1] = 1;
pathForSearch.push_back(tId1);
dfs(tId1);
pathForSearch.pop_back();
vis[tId1] = 0;
sort(res.begin(),res.end(),[](const auto &a,const auto &b){
int a1 = abs(a.first - 10000), a2 = abs(a.second - 10000);
int b1 = abs(b.first - 10000), b2 = abs(b.second - 10000);
if(a1 < b1) return true;
else if(a1 == b1 && a2 < b2) return true;
else return false;
});
cout<<res.size()<<endl;
for(auto p : res){
printf("%04d %04d\n",abs(p.first - 10000),abs(p.second - 10000));
}
}
}
题解
站在效率的角度出发,dfs本质上搜索了所有可能的路径,而递归的效率甚至差于迭代————嗨呀,那为什么不直接枚举呢。
首先用字符串来存储id的性别信息来排除0000的问题,然后通过在录入图的时候记录每个结点的同性别邻居————为的是在枚举时只枚举同性朋友。
然后用一个map来保存邻接信息————这里我用map是为了枚举的时候查询方便,不过空间开销会很大,如果用把二维id映射到一维数组的方法做hash的话当然是最好的,或者直接用邻接表在用的时候再次遍历查询也行。
最后再注意两点:
- 在枚举两人的同性朋友的过程中恰好枚举到了对方的情况要跳过。
- id是可能有前导0的四位id,不要因为样例而忽略了这点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int peopleCnt,relationCnt,queryCnt,tId1,tId2;
string sId1,sId2;
map<int,int> neighbor[20005];
vector<int> sameGenderNeighbor[20005];
vector<pair<int,int>> res;
int main(){
cin>>peopleCnt>>relationCnt;
for(int i = 0 ; i < relationCnt ; ++i){
cin>>sId1>>sId2;
tId1 = stoi(sId1) + 10000;
tId2 = stoi(sId2) + 10000;
neighbor[tId1][tId2] = neighbor[tId2][tId1] = 1;
if(sId1.size() == sId2.size()){
sameGenderNeighbor[tId1].push_back(tId2);
sameGenderNeighbor[tId2].push_back(tId1);
}
}
cin>>queryCnt;
for(int i = 0 ; i < queryCnt ; ++i){
cin>>tId1>>tId2;
tId1 += 10000;
tId2 += 10000;
res.clear();
//枚举两个人的同性朋友
for(int friend1 : sameGenderNeighbor[tId1]){
for(int friend2 : sameGenderNeighbor[tId2]){
if(friend1 == tId2 || friend2 == tId1) continue;
if(neighbor[friend1][friend2] == 1) res.push_back({abs(friend1 - 10000),abs(friend2 - 10000)});
}
}
sort(res.begin(),res.end(),less<>());
cout<<res.size()<<endl;
for(auto p : res){
printf("%04d %04d\n",p.first,p.second);
}
}
}