PAT 乙级 1065 单身狗 (25 分)


题目

“单身狗”是中文对于单身人士的一种爱称。本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱。

输入格式:
输入第一行给出一个正整数 N(≤ 50 000),是已知夫妻/伴侣的对数;随后 N 行,每行给出一对夫妻/伴侣——为方便起见,每人对应一个 ID 号,为 5 位数字(从 00000 到 99999),ID 间以空格分隔;之后给出一个正整数 M(≤ 10 000),为参加派对的总人数;随后一行给出这 M 位客人的 ID,以空格分隔。题目保证无人重婚或脚踩两条船。

输出格式:
首先第一行输出落单客人的总人数;随后第二行按 ID 递增顺序列出落单的客人。ID 间用 1 个空格分隔,行的首尾不得有多余空格。

输入样例:
3
11111 22222
33333 44444
55555 66666
7
55555 44444 10000 88888 22222 11111 23333
输出样例:
5
10000 23333 44444 55555 88888


分析

将每个ID值都看作int型,对于从 00000 到 99999的ID则对应的int为0-99999
一.创建字典 记录couple值
    ①创建一个dic[100000]的数组,初始化为-1(默认无couple)
    ②获取每一组couple的值,记录在数组中,以样例的第一组couple为例:
        dic[11111]=22222,dic[22222]=11111,如此记录。
二.创建guset[100000] 标记来宾ID
    ①创建一个guest[100000]的数组,初始化为0(默认ID对应的人没参加)
    ②获取参加派对人的ID,记录到guest数组中,即guest[i]++表示ID为i的人参加了派对
三.查询字典,将单身狗加入结果集合ans中
    1.遍历已经构造完成的guest数组,对于到来的客人(即guset[i]==1)分为以下两种情况
            ①dic[i]<0 ,此时来宾i无couple
            ②guest[dic[i]]==0 ,此时来宾i有couple,但其couple没参加
    2.将满足上述情况之一的便加入集合ans中
四.遍历集合ans输出结果

     ! 这里要注意用dic[i]<0 || guest[dic[i]] == 0将情况1写在前面并用 逻辑或 || ,因为逻辑或在运算中有短路特性,当 dic[i] < 0 时会结束整个运算, 排除了guest[-1]的下标越界的情况


代码

1.0版本

第一次AC的代码,最开始没考虑输出的不是五位数的情况,由于将字符串转化成了对应了整型,会导致左端缺0,没通过测试点3,用printf的格式化输出即可。

printf("%05d",*it);
#include<iostream>
#include<cmath>
#include<string>
#include<set>
using namespace std;
int TransToInt(string s){
    int sum=0;
    for(int i=0;i<s.length();i++){
        sum+=(s[i]-'0')*pow(10,4-i);
    }
    return sum;
}
int main(){
    int N,M,a,b,dic[100000];
    for(int i=0;i<100000;i++) dic[i]=-1;
    set<int> s,ans;
    string s1,s2;
    cin>>N;
    for(int i=0;i<N;i++){
        cin>>s1>>s2;
        a=TransToInt(s1);b=TransToInt(s2);
        dic[a]=b;dic[b]=a;
    }
    cin>>M;
    for(int i=0;i<M;i++){
        cin>>s1;
        s.insert(TransToInt(s1));
    }
    for(auto it=s.begin();it!=s.end();it++){
        if(s.find(dic[*it])==s.end()) ans.insert(*it);
    }
    printf("%d\n",ans.size());
    for(auto it=ans.begin();it!=ans.end();it++){
        if(it!=ans.begin()) cout<<" ";
        printf("%05d",*it);
    }
    return 0;
}

2.0版本

看了柳神的代码后的改进,其实最开始我也想到了多用一个数组存放guest的信息,但是没想好怎么之后处理,所以用了两个set。
与柳神思路略有不同,我这里多加了一个guest数组用于存放guest的信息,初始化为0,若对应位置的客人到来则变为1。之后对guest数组进行遍历,若guest[i]==0则跳过,对于值为1的位置,去dic[i]中寻找其对应的couple值,再根据guest[dic[i]]查询是否其couple也是来宾,若其couple没来则表示其落单,加入ans集合中。

#include<iostream>
#include<set>
#include<vector>
using namespace std;
int main(){
    int N,M,a,b;
    vector<int> dic(100000, -1),guest(100000,0);
    set<int> ans;
    scanf("%d", &N);
    for(int i = 0;i < N;i++){
        scanf("%d%d",&a,&b);
        dic[a]=b;dic[b]=a;
    }
    scanf("%d", &M);
    for(int i = 0;i < M;i++){
        scanf("%d",&a);
        guest[a]++;
    }
    for(int i = 0;i < 100000;i++){
        if(guest[i] == 0) continue;
        if(guest[dic[i]] == 0) ans.insert(i);
    }
    printf("%d\n",ans.size());
    for(auto it = ans.begin();it != ans.end();it++){
        if(it != ans.begin()) cout<<" ";
        printf("%05d",*it);
    }
    return 0;
}

3.0版本

排除下标存在越界可能后的最终版本

#include<iostream>
#include<set>
#include<vector>
using namespace std;
int main(){
    int N,M,a,b;
    vector<int> dic(100000, -1),guest(100000,0);
    set<int> ans;
    scanf("%d", &N);
    for(int i = 0;i < N;i++){
        scanf("%d%d",&a,&b);
        dic[a]=b;dic[b]=a;
    }
    scanf("%d", &M);
    for(int i = 0;i < M;i++){
        scanf("%d",&a);
        guest[a]++;
    }
    for(int i = 0;i < 100000;i++){
        if(guest[i] == 0) continue;
        if(dic[i]<0 || guest[dic[i]] == 0) ans.insert(i);
    }
    printf("%d\n",ans.size());
    for(auto it = ans.begin();it != ans.end();it++){
        if(it != ans.begin()) cout<<" ";
        printf("%05d",*it);
    }
    return 0;
}

总结

文章写着写着发现了一个问题,由于开始把couple字典的默认值设置为了-1,当guest[i] != 0(即i为来宾),对来宾i在字典中查询i的couple值时,若来宾i无coupl则返回的是-1,然后在guest中查询来宾i的couple是否到来是,实际上是通过guest[-1]的值来判断,此时下标为-1,很明显下标越界了,应该先判断来宾i存在couple的前提下(即 dic[i] != -1),再判断来宾中是否存在来宾i的couple(guest[dic[i]] == 0)
但是代码2.0又通过了所有测试…这就产生了一个问题,PAT内部的测试程序应该把vector越界的内容设置为了0,在自己电脑上测试时,代码2.0结果异常
在代码3.0中把 guest[dic[i]] == 0 改为了 dic[i]<0 || guest[dic[i]] == 0这里用了**||** 运用其短路计算的特性,在 dic[i]<0成立时便结束整个运算,排除了下标为-1的情况

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值