PAT.1139 First Contact

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);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值