• 作业9-STL专项练习8选6


    1.List

    描述

    写一个程序完成以下命令:
    new id ——新建一个指定编号为id的序列(id<10000)
    add id num——向编号为id的序列加入整数num
    merge id1 id2——合并序列id1和id2中的数,并将id2清空
    unique id——去掉序列id中重复的元素
    out id ——从小到大输出编号为id的序列中的元素,以空格隔开

    输入

    第一行一个数n,表示有多少个命令( n<=200000)。以后n行每行一个命令。

    输出

    按题目要求输出。

    样例输入

    16
    new 1
    new 2
    add 1 1
    add 1 2
    add 1 3
    add 2 1
    add 2 2
    add 2 3
    add 2 4
    out 1
    out 2
    merge 1 2
    out 1
    out 2
    unique 1
    out 1

    样例输出

    1 2 3 
    1 2 3 4
    1 1 2 2 3 3 4
    
    1 2 3 4

     1 #include<iostream>
     2 #include<list>
     3 #include<map>
     4 using namespace std;
     5 typedef map<int, list<int> >MAP;
     6 MAP m;
     7 int main(){
     8     int n;
     9     cin>>n;
    10     while(n--){
    11         string order;
    12         cin>>order;
    13         int id, num, id2;
    14         if(order=="new"){
    15             cin>>num;
    16             m.insert(MAP::value_type(num,list<int>()));
    17         }
    18         else if(order=="add"){
    19             cin>>id>>num;
    20             m[id].push_back(num);
    21         }
    22         else if(order=="merge"){
    23             cin>>id>>id2;
    24         m[id].merge(m[id2]);
    25         }
    26         else if(order=="unique"){
    27             cin>>id;
    28         m[id].sort();
    29         m[id].unique();
    30         }
    31         else if(order=="out"){
    32             cin>>id;
    33             m[id].sort();
    34             list<int>::iterator it;
    35             for(it = m[id].begin(); it != m[id].end(); it++)
    36                 cout<<*it<<" ";
    37             cout<<endl;
    38         }
    39     }
    40     return 0;
    41 }

    set、multiset、map、multimap这四个叫关联容器,内部元素都是有序排列的。而这里用到的map对象是<int, list<int> >类型的。map的插入有两种方式,这里用的是map<int, list<int>  >::value_type(int, list<int>()),下面题目里用的make_pair(int, int)也是一样的。

     list则是顺序容器,顺序容器是非排序的。下面标黄的几行都List的操作。list是双向链表,任何位置增删元素都能在常数时间完成。不支持随机存取。对比之下vector随机存取能在常数时间完成。但在中间插入元素复杂度就是n。

    list有一些独特的成员函数:

    要注意的就是list有自己特殊的sort,并且在用unique之前要先sort。

     

     

    2.RPN calculator

    描述

    Reverse Polish notation (or just RPN) by analogy with the related Polish notation, a prefix notation introduced in 1920 by the Polish mathematician Jan Łukasiewicz, is a mathematical notation wherein every operator follows all of its operands. It is also known as Postfix notation.

    In Reverse Polish notation the operators follow their operands; for instance, to add three and four one would write "3 4 +" rather than "3 + 4". If there are multiple operations, the operator is given immediately after its second operand; so the expression written "3 − 4 + 5" in conventional infix notation would be written "3 4 − 5 +" in RPN: first subtract 4 from 3, then add 5 to that. An advantage of RPN is that it obviates the need for parentheses that are required by infix. While "3 − 4 * 5" can also be written "3 − (4 * 5)", that means something quite different from "(3 − 4) * 5", and only the parentheses disambiguate the two meanings. In postfix, the former would be written "3 4 5 * −", which unambiguously means "3 (4 5 *) −".

    You were asked to design a simple RPN calculator, which will support the “+”, “-“, “*”, “/”(the absolute value of the divisor will not less then 10^-9) and “^”(power operator, if the base number b<=0, the exponential e must be a positive integer not greater than 10^9) operators. You can assume all the numbers during the calculation can fit into a double-precision floating point number.

    In addition, our calculator has some memory. Each time we calculate an expression, the smallest number in the memory will be erased, and replace it with the value of the expression.

    输入

    The first line contains an integer n, which is the memory size of our calculator.

    From the second line, we will give n numbers, which is the initial value of the memory. each line except last will have 10 numbers.

    And then each line has a valid RPN expression we previously described, end with “=”, which is the command for calculation. Each term will no longer than 20 characters.

    输出

    For each expression, output the value of it in a line.
    And then output an empty line to separate the two parts.
    At last, output the all the numbers in memory, in increasing order, 10 numbers per line.

    Each number should be formatted in scientific notation with 6 digits after decimal point and 2 digits of exponential, such like “%e” format string of printf() function in C. The numbers in a line should be separated by a space.

    样例输入

    4
    1e6 1e-6 0.001 1000
    1 2 + 3 4 + * =
    1 0.1 / 8 ^ =

    样例输出

    2.100000e+01
    1.000000e+08
    
    2.100000e+01 1.000000e+03 1.000000e+06 1.000000e+08

    提示

    Huge input, scanf() is recommended

    %e格式输出在windows环境下指数部分为3位,在系统的测试环境下为2位。

     1 #include<iostream>
     2 #include<stack>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<cmath>
     6 #include<set>
     7 #include<string>
     8 #include<sstream> 
     9 using namespace std;
    10 char a[100][25];
    11 multiset<double>p; //应该是multiset!! 
    12 stack<double>s;
    13 int main(){
    14     //freopen("in.txt","r", stdin);
    15     string str;
    16     int n, i;
    17     scanf("%d", &n);
    18     double num;
    19     for(i = 0; i < n; i++){
    20         scanf("%lf", &num);
    21     //    printf("%e ", num); //只能用来控制输出 
    22         p.insert(num);
    23     }
    24     while(getline(cin,str)){
    25         if(str=="")continue;
    26         istringstream iss(str);
    27         int len = 0;
    28         memset(a,0,sizeof(a));
    29         while(!s.empty())s.pop();
    30         while(!iss.eof()){
    31             iss>>a[len++];
    32         }
    33         for(i = 0; i < len-1; i++){
    34             if(atof(a[i])!=0) s.push(atof(a[i]));
    35             else{
    36                 string op = a[i];
    37                 double d1 = s.top();
    38                 s.pop();
    39                 double d2 = s.top();
    40                 s.pop();
    41                 if(op=="*")d2*=d1;
    42                 else if(op=="+") d2+=d1;
    43                 else if(op == "-") d2-=d1;
    44                 else if(op == "/") d2/=d1;
    45                 else if(op == "^") d2 = pow(d2,d1);
    46                 s.push(d2);
    47             }
    48         }
    49         printf("%e
    ", s.top());
    50         p.erase(p.begin());
    51         p.insert(s.top());
    52     }
    53     printf("
    ");
    54     set<double>::iterator it;
    55     int count = 0;
    56     for(it = p.begin(); it!=p.end(); it++){
    57         count++;
    58         if(count%10==0)
    59             printf("%e
    ", *it);
    60         else printf("%e ", *it);
    61     }    
    62     return 0;
    63 }

    这道题就就是波兰表达式。参考了我之前的前缀表达式那道题的写法,就是用到了字符串输入和atof(还能识别负数非常好用)。需要注意的几点是:

    1.scanf输入double类型变量,要用%lf,这个自动就能识别科学计数法,%e只能用来控制输出!

    2.因为某种原因while(getline)会先读入一个空的字符串,我也不知道为啥,所以加了continue那一行。

    3.用priority_queue应该也可以,就是我中间傻了。set也是自动排序没毛病。要注意的是要用multiset!!这是我最后一个错误,在用set的时候一定要想想元素是不是互不相同的。

    4.最后就是仔细读题,输出有格式要求,十个数换一行,这个我开始也没注意。。

    3.Set

    描述现有一整数集(允许有重复元素),初始为空。我们定义如下操作:
    add x 把x加入集合
    del x 把集合中所有与x相等的元素删除
    ask x 对集合中元素x的情况询问
    对每种操作,我们要求进行如下输出。
    add 输出操作后集合中x的个数
    del 输出操作前集合中x的个数
    ask 先输出0或1表示x是否曾被加入集合(0表示不曾加入),再输出当前集合中x的个数,中间用空格格开。

    输入

    第一行是一个整数n,表示命令数。0<=n<=100000。
    后面n行命令,如Description中所述。

    输出

    共n行,每行按要求输出。样例输入

    7
    add 1
    add 1
    ask 1
    ask 2
    del 2
    del 1
    ask 1

    样例输出

    1
    2
    1 2
    0 0
    0
    2
    1 0

    提示

    Please use STL’s set and multiset to finish the task

     1 #include<iostream>
     2 #include<set>
     3 using namespace std;
     4 multiset<int>ms;
     5 set<int>s;
     6 int main(){
     7     int n;
     8     cin>>n;
     9     while(n--){
    10         string order;
    11         int num;
    12         cin>>order>>num;
    13         if(order=="add"){
    14             s.insert(num);
    15             ms.insert(num);
    16             cout<<ms.count(num)<<endl;
    17         }
    18         else if(order=="ask"){
    19             if(s.find(num)!=s.end()) cout<<"1 ";
    20             else cout<<"0 ";
    21             cout<<ms.count(num)<<endl;
    22         }
    23         else if(order=="del"){
    24             cout<<ms.count(num)<<endl;
    25             multiset<int>::iterator it;
    26             it = ms.find(num);
    27             while(it != ms.end()){
    28                 ms.erase(it);
    29                 it = ms.find(num);
    30             }
    31         }
    32     }
    33     return 0;
    34 }

    提示里面其实也写了。巧妙的地方就在于同时用了multiset和set,用set来记录一个数是否曾经被加入过multiset里。

    4.热血格斗场

    描述

    为了迎接08年的奥运会,让大家更加了解各种格斗运动,facer新开了一家热血格斗场。格斗场实行会员制,但是新来的会员不需要交入会费,而只要同一名老会员打一场表演赛,证明自己的实力。

    我们假设格斗的实力可以用一个正整数表示,成为实力值。另外,每个人都有一个唯一的id,也是一个正整数。为了使得比赛更好看,每一个新队员都会选择与他实力最为接近的人比赛,即比赛双方的实力值之差的绝对值越小越好,如果有两个人的实力值与他差别相同,则他会选择比他弱的那个(显然,虐人必被虐好)。

    不幸的是,Facer一不小心把比赛记录弄丢了,但是他还保留着会员的注册记录。现在请你帮facer恢复比赛纪录,按照时间顺序依次输出每场比赛双方的id。

    输入

    第一行一个数n(0 < n <=100000),表示格斗场新来的会员数(不包括facer)。以后n行每一行两个数,按照入会的时间给出会员的id和实力值。一开始,facer就算是会员,id为1,实力值1000000000。输入保证两人的实力值不同。

    输出

    N行,每行两个数,为每场比赛双方的id,新手的id写在前面。

    样例输入

    3
    2 1
    3 3
    4 2

    样例输出

    2 1
    3 2
    4 2
     1 #include<iostream>
     2 #include<map>
     3 #include<cstdio>
     4 #include<cmath>
     5 using namespace std;
     6 
     7 int main(){
     8     int n;
     9     scanf("%d", &n);
    10     multimap<int, int>m;
    11     m.insert(make_pair(1000000000,1)); //需要按实力排序 
    12     int id, cp; 
    13     multimap<int, int>::iterator i, t1, t2; 
    14     while(n--){
    15         scanf("%d %d", &id, &cp);
    16         i = m.insert(make_pair(cp, id));
    17         printf("%d
    ",i); 
    18         if(i!=m.begin()&&i!=--m.end()){
    19             t1 = --i;//list容器的迭代器不支持加减等数术运算,因为list容器不支持随机访问
    20             i++; //必须得放在里面写? 
    21             t2 = ++i;
    22             i--;
    23             if(abs(t1->first-cp)<=abs(t2->first-cp))
    24                 printf("%d %d
    ", id, t1->second);
    25             else printf("%d %d
    ", id, t2->second);
    26         }
    27         else if(i == m.begin()) printf("%d %d
    ", id, (++i)->second);
    28         else if(i == --m.end()) printf("%d %d
    ", id, (--i)->second);
    29     }
    30     return 0;
    31 }

    这道题好像是为冷血格斗场做铺垫。要注意的一点就是(我也是问了助教),迭代器一旦越界,再用++或者--操作就会导致它gg了。反正养成良好习惯不要让迭代器瞎指了。

    5.冷血格斗场

    描述

    为了迎接08年的奥运会,让大家更加了解各种格斗运动,facer新开了一家冷血格斗场。格斗场实行会员制,但是新来的会员不需要交入会费,而只要同一名老会员打一场表演赛,证明自己的实力。

    我们假设格斗的实力可以用一个正整数表示,成为实力值,两人的实力值可以相同。另外,每个人都有一个唯一的id,也是一个正整数。为了使得比赛更好看,每一个新队员都会选择与他实力最为接近的人比赛,即比赛双方的实力值之差的绝对值越小越好,如果有多个人的实力值与他差别相同,则他会选择id最小的那个。

    不幸的是,Facer一不小心把比赛记录弄丢了,但是他还保留着会员的注册记录。现在请你帮facer恢复比赛纪录,按照时间顺序依次输出每场比赛双方的id。

    输入

    第一行一个数n(0 < n <=100000),表示格斗场新来的会员数(不包括facer)。以后n行每一行两个数,按照入会的时间给出会员的id和实力值。一开始,facer就算是会员,id为1,实力值1000000000。

    输出

    N行,每行两个数,为每场比赛双方的id,新手的id写在前面。

    样例输入

    3
    2 3
    3 1
    4 2

    样例输出

    2 1
    3 2
    4 2
     1 #include<iostream>
     2 #include<map>
     3 #include<cstdio>
     4 #include<cmath>
     5 using namespace std;
     6 
     7 int main(){
     8     int n;
     9     scanf("%d", &n);
    10     map<int, int>m; //不需要multimap
    11     int id, cp; 
    12     m[1000000000] = 1;
    13     map<int, int>::iterator i, t1, t2; //map的insert函数返回的是pair 
    14     while(n--){
    15         scanf("%d %d", &id, &cp);
    16         i = m.find(cp);
    17         if(i != m.end()){
    18             printf("%d %d
    ", id, i->second);
    19             if(id < i->second) m[cp] = id;
    20         }
    21         else {
    22             m.insert(make_pair(cp,id));    
    23             i = m.find(cp); 
    24             if(i!=m.begin()&&i!=--m.end()){
    25                 t1 = --i;
    26                 i++; 
    27                 t2 = ++i;
    28                 i--;
    29                 if(abs(t1->first-cp)<abs(t2->first-cp))
    30                     printf("%d %d
    ", id, t1->second);
    31                 else if(abs(t1->first-cp)>abs(t2->first-cp)) printf("%d %d
    ", id, t2->second);
    32                 else printf("%d %d
    ", id, min(t1->second, t2->second));
    33             }
    34             else if(i == m.begin()) printf("%d %d
    ", id, (++i)->second);
    35             else if(i == --m.end()) printf("%d %d
    ", id, (--i)->second);
    36         }    
    37     } 
    38     return 0;
    39 }

    这道题和热血格斗场的区别就是在实力相差相同的情况下,冷血格斗场会选择id最小的。这道题妙处就在于用map就可以了。如果发现一个实力值相同的新人,(就可以直接输出这一对比赛)然后保留id最小的就可以,因为id大的永远也没机会上场了。

    6.priority_queue练习题

    描述

    我们定义一个正整数a比正整数b优先的含义是:
    *a的质因数数目(不包括自身)比b的质因数数目多;
    *当两者质因数数目相等时,数值较大者优先级高。

    现在给定一个容器,初始元素数目为0,之后每次往里面添加10个元素,每次添加之后,要求输出优先级最高与最低的元素,并把该两元素从容器中删除。

    输入

    第一行: num (添加元素次数,num <= 30)

    下面10*num行,每行一个正整数n(n < 10000000).

    输出

    每次输入10个整数后,输出容器中优先级最高与最低的元素,两者用空格间隔。

    样例输入

    1
    10 7 66 4 5 30 91 100 8 9

    样例输出

    66 5
     1 #include<iostream>
     2 #include<set>
     3 #include<cstdio>
     4 #include<cmath>
     5 #include<queue>
     6 #include<cstring>
     7 using namespace std;
     8 
     9 bool isprime(int n){
    10     if(n==2||n==3) return true;
    11     for(int i = 2; i < sqrt(n)+1; i++){
    12         if(n%i==0) return false;
    13     }
    14     return true;
    15 }
    16 int factor(int n){
    17     int sum = 0;
    18     for(int i = 2; i <= sqrt(n); i++){
    19         if(n%i==0){
    20             int k = n/i;
    21             if(i!=k&&isprime(k)) sum++;
    22             if(isprime(i)) sum++;
    23         }
    24     } 
    25     return sum;
    26 }
    27 
    28 struct cmp{
    29     bool operator()(int a, int b){
    30         int numa = factor(a),numb = factor(b);
    31         if(numa!=numb) return numa<numb;
    32         else return a<b;
    33     }
    34 };
    35 
    36 int main(){
    37     set<int, cmp> queue;
    38     int n = 0;
    39     scanf("%d", &n);
    40     while (n--)
    41     {
    42         int num;
    43         for (int i = 0; i < 10; i++)
    44         {
    45             scanf("%d", &num);
    46             queue.insert(num);
    47         }
    48  
    49         int min = *(queue.begin());
    50         int max = *(queue.rbegin());
    51         queue.erase(max);
    52         queue.erase(min);
    53         printf("%d %d
    ",max, min);
    54     }
    55     return 0;
    56 }

    这道题参照了网络代码……虽然是优先队列但其实用的是set来实现,因为又要求取最大又要求最小。注意set的第二个参数需要是个函数对象,来告诉set什么是“<”。还有就是通过标黄代码操作,统计质因数时循环到sqrt(n)就可以了。

    set和map都有这三种erase:

    (1)
    iterator  erase (const_iterator position);
    
    (2)
    size_type erase (const key_type& k);
    
    (3)
    iterator  erase (const_iterator first, const_iterator last);

    所以这就是为什么根据值删掉max和min是可行的。但要注意这种删除方式复杂度是log。

    
    
  • 相关阅读:
    常用的虚拟化产品
    KVM虚拟化
    Jenkins流水线项目发布流程
    Jenkins安装,界面说明
    敏捷估算与规划—总结
    敏捷估算与规划—跟踪与交流
    敏捷估算与规划—进度安排
    敏捷估算与规划—为价值做规划
    敏捷估算与规划—问题与目标
    用户故事与敏捷方法笔记 --- 估算与计划
  • 原文地址:https://www.cnblogs.com/fangziyuan/p/12640945.html
Copyright © 2020-2023  润新知