【刘汝佳书】习题5-4 UVA10763(multiset+判重)

【2019.4.7】库里FMVP

需要注意的地方:

1、书上翻译的有些问题,每行输入的两个数字分别代表了学校,而不是学生本身,因此允许有下面这样的数据出现(学校1和学校4有两对学生交换,输出YES)
1 4
1 4
4 1
4 1

2、每行的两个数字顺序是固定的,第一个数字是原来的学校,第二个数字是想去的学校

思路整理:
1、对于大量输入数据的题目,有两种思路:

① 一边输入一边对数据做处理,输入完了之后自然得到结果
② 输入全部数据之后,再做处理,得到结果

由于输入数据有500000个之多,第一种可能性更大一些,顺着第一种思路往下想:

2、一边输入,一边对数据需要做哪些处理?

根据题意可知,我们需要判断,对于每一个输入(a,b),是否能找到另一个输入(b,a)与之对应
换句话说,对于每一个输入(a,b),如果我们先将他调换过来形成(b,a),然后再找是否存在另一个(b,a)与之完全相同,这就相当于一个判重问题

3、判重问题

判重问题常常需要一个集合(set),当我们每输入一行(a,b),我们将在集合(set)中查找其调换过来的形式(b,a)
如果在集合中找到了,我们把(b,a)从集合中删去,就好像连连看游戏那样
如果没在集合中找到,我们就把其原形式(a,b)插入集合,等待其他输入数据与其对应

4、选择数据结构

我们需要一个集合,集合的元素是二元组,对于集合的操作是:频繁的插入、删除和查找

首先,对于二元组,有数组、结构体、vector等等形式,其中vector可以作为其他容器的基本元素,况且也定义了各种运算符,因此比较合适

其次,对于集合,我们通常选用set,其特点是:内部排序,插入和删除比较快,查找相对快
然而这道题是不能用set的,因为set里不会出现重复元素,而我们的数据里可能会出现重复的二元组
因此我们选用multiset,它跟set唯一的区别就是,允许有重复元素的存在

5、判断输出结果

输出YES的情况:所有数据输入完毕,集合为空,证明所有元素配对成功
其他情况都输出NO

6、有一些早期判断结果的条件

① 如果n为奇数的话,那么一定输出NO,因为此时不可能配对
② 当还剩下x个二元组没输入,集合中还有y个元素时,如果x<y,那么一定输出NO,因为集合里那y个元素不可能全都找到配对,一定会有剩下的

7、需要注意的问题

当我们用multiset.erase(x)来删除集合中的x元素时,集合中所有x元素都会被删除,而本题中,我们一次只需要删除其中一个,这时候就要用到iterator

代码:
#include <iostream>
#include <set>
#include <map>
#include <vector>

using namespace std;

multiset<vector<int>> s;	//判重集合
vector<int> p1;		//原始输入
vector<int> poff;	//两个数字调过来的输入

int main()
{
    //freopen("C:\\Users\\Summer\\Desktop\\input.txt", "r", stdin);
    //freopen("C:\\Users\\Summer\\Desktop\\output.txt", "w", stdout);

    int n;
    bool flag;
    while(cin>>n && n) {
        flag = true;
        s.clear();

        //如果有奇数个,永远不可能配对
        if(n%2 == 1) flag = false;

        //输入
        int a, b;
        int i;
        for(i=0; i<n; i++) {
            scanf("%d %d", &a, &b);
			
			//原始的二元组(a,b)
            p1.clear(); p1.push_back(a); p1.push_back(b);
			//把a和b调换顺序,形成二元组(b,a)
            poff.clear(); poff.push_back(b); poff.push_back(a);

			//it用于指向待删除元素
            multiset<vector<int>>::iterator it;
            if(flag) {
                //如果配对就移除
                if(s.count(poff)) {
                    it = s.find(poff);	//找到集合中与当前输入配对的元素(b,a)
                    s.erase(it);		//删除之
                }
                //否则就插入
                else s.insert(p1);		//插入了原始二元组(a,b)

                //如果剩下输入的数量比还未配对的数量少,一定输入NO
                if(n-i-1 < (int)s.size()) flag = false;
            }
        }

        //当所有输入数据都完事,集合是否为空
        if(s.size()) flag = false;

        //输出
        if(flag) cout<<"YES"<<endl;
        else     cout<<"NO"<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值