zoukankan      html  css  js  c++  java
  • 算法竞赛入门经典_5 c++与STL入门

    直接跳到第五章了

    c语言是一门很有用的语言,但在算法竞赛中却不流行,原因在于它太底层,缺少一些实用的东西。

    下面是一个简单的c++框架

    #include<cstdio>
    
    int main()
    {
        int a, b;
        while(scanf("%d%d", &a, &b) == 2)
            printf("%d
    ", a+b);
        return 0;
    }

    注:其中c++中的头文件使用sctdio代替stdio.h,cstring代替string.h,cmath代替math.h,cctype代替ctype.h

    运行效果

    下面是更复杂的:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int maxn = 100 + 10;
    int A[maxn];
    int main()
    {
    //    long long a, b;
    //    _int64 a, b;
        int a, b;
        while(cin >> a >> b)
        {
            //使用_MIN代替min,因为algorithm中没有min函数
            cout <<_MIN(a, b) <<endl;
        }
        return 0;
    }

    注:在vc6++不支持long long 类型,但是支持_int64的声明,不过在cin>>a>>b中会报错,因为不支持

    运行效果

      c++还支持引用类型

    #include <iostream>
    using namespace std;
    
    void swap2(int &a, int &b)
    {
        int t = a;
        a = b;
        b = t;
    }
    
    int main()
    {
        int a = 3, b = 4;
        swap2(a, b);
        cout<< a << " "<< b << endl;
        return 0;
    }

    运行效果:

        c++中对字符串的处理

    #include <iostream>
    #include <string>
    #include <sstream>
    
    using namespace std;
    int main()
    {
        string line;
        while(getline(cin, line))
        {
            int sum = 0;
            int x;
            stringstream ss(line);
            while(ss>>x)
                sum= sum + x;
            cout << sum << endl;
        }
        return 0;
    }

    运行效果

    c++中结构体

    #include <iostream>
    using namespace std;
    
    struct Point{
    int x, y;
    Point(int x=0, int y=0 ):x(x),y(y){}
    };
    Point operator + (const Point &A, const Point &B){
        return Point(A.x + B.x, A.y + B.y);
    }
    
    ostream& operator << (ostream &out, const Point &p){
        out<< "("<< p.x << "," <<p.y<< ")";
        return out;
    }
    
    int main()
    {
        Point a, b(1,2);
        a.x = 3;
        cout << a + b<<endl;
        return 0;
    }

      首先,c++不需要使用typedef来定义一个struct了,而且在struct中,除了可以有变量,还可以有函数

    其中,:x(x),y(y)是“Point {int x =0, int y = 0}{this->x = x;this->y = y;}”的简写

    此外,上面代码还使用了重载运算符

    运行结果:

    c++模板的使用:

    #include<iostream>
    using namespace std;
    template<typename T>
    T sum(T *begin, T *end)
    {
        T *p = begin;
        T ans = 0;
        for(; p != end; p++)
            ans = ans + *p;
        return ans;
    }
    
    int main()
    {
        double a[] = {1.1, 2.2, 3.3, 4.4};
        cout << sum(a , a+4) << endl;
        return 0;
    }

     其中:使用template <typename T>声明一个模板T

    运行效果:

     c++的sort排序

    题目:

    /*
    大理石在哪儿问题
    
      现有N个大理石,每个大理石上写了一个非负整数。首先把个数从小到大排序,然后回答
      Q个问题,每个问题问是否有一个大理石写着某个整数x,如果是,还要回答哪个石上写
      着x.排序后的大理石从左到右编号为1-N。
      输入:
      4 1
      2 3 5 1
      5
      5 2
      1 3 3 3 1
      2 3
      输出:
      CASE# 1:
      5 found at 4
      CASE# 2:
      2 not found
      3 found at 3
    */

    代码:

    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int maxn = 10000;
    
    int main()
    {
        int n, q, x, a[maxn], kase = 0;
        while(scanf("%d%d", &n, &q) == 2 && n){
            printf("CASE# %d:
    ", ++kase);
            for(int i = 0; i < n; i++)
                cin>>a[i];
        
                sort(a, a+n);
    
                while(q--){
                    scanf("%d", &x);
                    int p = lower_bound(a, a+n, x) - a;
                    if(a[p] == x)
                        printf("%d found at %d
    ", x, p+1);
                    else printf("%d not found
    ", x);
                }
        }
        return 0;
    }

    分析:sort函数可以对任意对象进行排序,参数1是数组起始地址,参数2是数组结尾地址,当然也可以定义自己的比较函数。

    lower_bound函数是查找大于或等于x的第一个位置。参数1,2分别是起始和结束地址,参数3是待查数字x.

    unique函数可以删除有序数组中的重复元素。

    运行效果:

    c++中的可变数组vector

    问题:

    /*
    木块问题
    从左到右有n个木块,编号为0--n-1,要求模拟以下4种操作(a和b都是编号)
    move a onto b; 把a和b上方的木块全部归位,然后把a摞在b上面。
    move a over b: 把a上方的木块全部归位,然后把a放在b所在木块的的顶部
    pile a onto b: 把b上方的木块全部归位,然后把a和a上面的木块整体摞在b上面
    pile a over b:把a及上面的木块整体摞在b上方的堆的顶部
    */

    代码:

    #include <iostream>
    #include <algorithm>
    
    #include <string>
    #include <vector>
    using namespace std;
    
    const int maxn = 30;
    int n;
    vector<int> pile[maxn];//每个pile[i]是一个vector
    
    //找木块a所在的pile和height,以引用的形式返回调用者
    
    void find_block(int a, int &p, int &h)
    {
        for(p = 0; p < n;p++)
            for(h = 0; h < pile[p].size(); h++)
                if(pile[p][h] == a)return;
        
    }
    
    //把第p堆高度为h的木块上方的所有木块移回原位
    void clear_above(int p, int h){
        for(int i= h+1; i < pile[p].size(); i++)
        {
            int b = pile[p][i];
            pile[b].push_back(b);//把木块b放回原位
        }
        pile[p].resize(h+1); //pile只应保留下标0-h的元素
    }
    
    //把第p堆高度为h及上方的木块整体移动到p2堆得顶部
    void pile_onto(int p, int h, int p2)
    {
        for(int i = h; i < pile[p].size(); i++)
            pile[p2].push_back(pile[p][i]);
        pile[p].resize(h);
    }
    
    void print()
    {
        for(int i = 0; i < n; i++)
        {
            printf("%d:", i);
            for(int j = 0; j < pile[i].size(); j++)
                printf("
    ");
        
        }
        
    }
    
    int main()
    {
        int a, b;
        cin >> n;
        string s1, s2;
        for(int i = 0; i < n; i++)
            pile[i].push_back(i);
        while(cin >> s1 >> a >> s2 >> b)
        {
            int pa, pb, ha, hb;
            find_block(a, pa, ha);
            find_block(b, pb, hb);
            if(pa == pb) continue; //非法指令,忽略
            if(s2 == "onto")
                clear_above(pb, hb);
            if(s1 == "move")
                clear_above(pa, ha);
            pile_onto(pa, ha, pb);
        }
        print();
        return 0;    
    }

    分析:上面的代码有一个值得学习的技巧:输入一共4条指令,处理这种问题的更好的方法是  提取出指令键的共同点,编写函数以减少重复代码。

    clear()可以清空,resize()可以改变大小,push_back()和pop_back()分别是在尾部进行添加和删除元素。empty()是测试是否为空。

    运行效果:

    c++中的集合set

    问题:

    /*
        安迪的第一个字典
        输入一个文本,找出所有不同的单词(连续的字母序列), 按字典序从小到大输出,单词不区分大小写。
        输入:
        Adventures in Disneyland
    
      Two blondes were going to Disneyland when they came to a fork in the road.The sign read :"Disneyland Left.";
    
      So they go home.
    
      输出:
      a
      adventures
      blondes
      came
      disneyland
    */

    代码:

    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<set>
    #include<sstream>
    using namespace std;
    
    set<string> dict; //string 字典集合
    int main()
    {
        string s, buf;
        while(cin >> s)
        {
            for(int i = 0; i < s.length(); i++)
                if(isalpha(s[i])) s[i] = tolower(s[i]);else s[i] = ' ';
                stringstream ss(s);
                while(ss >> buf)
                    dict.insert(buf);
        }
    
        for(set<string>::iterator it = dict.begin(); it != dict.end(); ++it)
            cout<< *it <<endl;
        return 0;
    }

    分析:isalpha()判断是否是一个字母,tolower函数转化为统一的小写

      stringstream是输入输出流,进行类型转化,insert()在集合中添加,

      iterator是set对象中的迭代器,begin()和end()是指向开始和结束

      *it取得当前对象的值

    运行效果:

    c++中的映射map:

    问题:

    /**
        反片语
    
        输入一些单词,找出所有满足如下条件的单词:该单词不能通过字母重排,得到输入的
        文本中的另外一个单词。在判断是否满足条件时,字母不区分大小写,但在输出是应
        保留输入中的大小写,按字典序进行排序(所有大写字母在小写字母的前面)
        输入:
        ladder came tape soon leader acme RIDE lone Disk derail peat
        ScALE orb eye Rides dealer NotE LaCeS drIed noel dire mace Rob dries
        #
        输出:
        Disk
        Note
        derail
        drIed
        eye
        ladder
        soon
    */

    代码:

    #include<iostream>
    #include<string>
    #include<cctype>
    #include<vector>
    #include<map>
    #include<algorithm>
    using namespace std;
    
    map<string, int> cnt;
    vector<string> words;
    
    //将单词s标准化
    string repr(const string &s) {
        string ans = s;
        for (int i = 0; i < ans.length(); i++) {
            ans[i] = tolower(ans[i]);
        }
        sort(ans.begin(), ans.end());
        return ans;
    }
    int main(){
        
        int n = 0;
        string s;
        while (cin >> s)
        {
            if (s[0] == '#') break;
            words.push_back(s);
            string r = repr(s);
            //判断cnt中是否存在键r
            if (!cnt.count(r)) cnt[r] = 0;
            cnt[r]++;
        }
        vector<string> ans;
        for (int i = 0; i < words.size(); i++)
            if (cnt[repr(words[i])] == 1)
                ans.push_back(words[i]);
        sort(words.begin(), words.end());
        for (int i = 0; i < ans.size(); i++)
            cout << ans[i] << endl;
         return 0;
    }

    分析:map有键和值,key value,同样和set集合有类似的insert,find,count和remove操作,此外map还提供了[]运算符

    insert是插入键值,find是查找指定位置的值,count是查找值是否存在,remove删除。

    运行效果:

     c++中的栈

    stack<int> s;

    push()入栈,pop()出栈,top()取栈顶元素

    c++中的队列

    queue<int> s; push入队,pop出队,front取队首元素。

    问题

    /*
        团体队列
        有t个团队正在排一个长队,每次新来一个人时,如果他有队友在排队,
        那么这个新人会插到最后一个队友的身后。如果没有任何一个队友排队
        ,则他会排到长队的队尾。
        输入每个团队中所有队员的编号,要求支持如下3种指令(前两种可以穿插进行)
        ENQUEUE x: 编号为x的人进入长队
        DEQUEUE :长队的队首出队
        STOP : 停止模拟
        对于每个DEQUEUE    指令,输出出队的人的编号
    
    */

    代码:

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<map>
    using namespace std;
    
    const int maxt = 1000 + 10;
    int main() {
        int t, kase = 0;
        while (scanf("%d", &t) == 1 && t)
        {
            printf("Scenario #%d
    ", ++kase);
            //记录所有人的团队编号
            map<int, int> team;    //team[x]表示编号为x的人所在的团队编号
            for (int i = 0; i < t; i++) {
                int n, x;
                scanf("%d", &n);
                while (n--) {
                    scanf("%d", &x);
                    team[x] = i;
                }
            }
    
            //模拟
            queue<int> q, q2[maxt];    //q是团队队列,而q2[i]是团队i成员的队列
            for (;;) {
                int x;
                char cmd[10];
                scanf("%s", cmd);
                if (cmd[0] == 'S') break;
                else if (cmd[0] == 'D'){
                    int t = q.front();
                    printf("%d
    ", q2[t].front());
                    q2[t].pop();
                    if (q2[t].empty())
                        q.pop();//团体t全体出队列
                }
                else if (cmd[0] == 'E') {
                    scanf("%d", &x);
                    int t = team[x];
                    if (q2[t].empty())
                        q.push(t);//团队t进入队列
                    q2[t].push(x);
                }
            }
            printf("
    ");
        }
    
        getchar();
        getchar();
    
        return 0;
    }

    分析:

    首先进行的是记录所有大团队的编号

    用一个team映射键保存每个队的成员编号,值保存每个队的序号。team[编号] = 队序号

    然后是模拟阶段,定义了一个q来保存大团队的队列,q2保存个成员所在队列。

    分析好了这个,下面就是各种命令就好办了。

    运行效果

     c++中的优先队列

    问题

    /*
        丑数
        丑数是指不能被2,3,5以外的其他素数整除的数。把丑数从小到大排列起来,结果如下
        1,2,3,4,5,6,7,8,9,10,12,15,...
        求第1500个丑数
    */

    代码

    #include<iostream>
    #include<functional>
    #include<vector>
    #include<queue>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int coeff[3] = {2, 3, 5};//系数
    
    int main() {
        
        priority_queue < LL, vector<LL>, greater<LL> > pq;
        set<LL> s;
        pq.push(1);
        s.insert(1);
        for (int i = 1; ; i++) {
            LL x = pq.top();
            pq.pop();
            if (i == 1500) {
                cout << x << endl;
                break;
            }
            for (int j = 0; j < 3; j++) {
                LL x2 = x * coeff[j];
                if (!s.count(x2)) {
                    s.insert(x2);
                    pq.push(x2);
                }
            }
        }
        getchar();
        return 0;
    }

    分析:

    越小的整数优先级越大的优先队列可以用queue头文件中的priority_queue<int, vector<int>, greater<int> > pq;

    注意:greater比较模板在functional头文件中,结尾的 > >不能写在一起

    先定义一个优先队列pq来保存所有已生成的丑数,集合s来保存所有的丑数

    然后取出队首元素,这里是取出一个数然后生成3个丑数,并判断是否已经生成过。

    答案:859963392

    stl测试

    先看代码:

    #include<iostream>
    #include<cstdlib>
    #include<vector>
    #include<algorithm>
    #include<cassert>
    #include<ctime>
    using namespace std;
    
    void fill_random_int(vector<int>& v, int cnt) {
        v.clear();
        for (int i = 0; i < cnt; i++)
            v.push_back(rand());
    }
    void test_sort(vector<int>& v) {
        sort(v.begin(), v.end());
        for (int i = 0; i < v.size() - 1; i++)
        assert(v[i] <= v[i+1]);
    }
    int main() {
        srand(time(NULL));
        vector<int> v;
        fill_random_int(v, 1000000);
        test_sort(v);
        getchar();
        return 0;
    }

    分析:库不一定没有bug,我们要学会如何测试一个库

    首先需要用到随机数,rand()函数实在cstdlib中,使用前需要srand(time(NULL))初始化随机种子

    我们定义一个函数fill_random_int来网vector可变数组中存放随机数,void clear():  函数clear()删除储存在vector中的所有元素. 

    assert作用是:当表达式为真时无变化,但当表达式为假时强行终止程序

    算法题:

    (1)Unix ls命令

    问题

    /*
        Unix ls命令问题
        输入正整数n以及n个文件名,排序后按列优先方式左对齐输出。
        假设最长文件名有M字符,则最右列有M字符,其他列都是M+2字符
    
    */

    代码

    #include<iostream>
    #include<string>
    #include<algorithm>
    using namespace std;
    
    const int maxcol = 60;
    const int maxn = 100 + 5;
    string filename[maxn];
    
    //输出字符串s,长度不足len时补字符extra
    void print(const string& s, int len, char extra) {
        cout << s;
        for (int i = 0; i < len - s.length(); i++)
            cout << extra;
    }
    int main() {
        int n;
        while (cin >> n) {
            int M = 0;
            for (int i = 0; i < n; i++) {
                cin >> filename[i];
                M = max(M, (int)filename[i].length());
    
            }
            //计算列数cols和行数rows
            int cols = (maxcol - M) / (M + 2) + 1, rows = (n - 1) / cols + 1;
            print("", 60, '-');
            cout << endl;
            sort(filename, filename + n);
            for (int r = 0; r < rows; r++)
            {
                for (int c = 0; c < cols; c++) {
                    int idx = c * rows + r;
                    if (idx < n)
                        print(filename[idx], c == cols - 1 ? M : M+2, ' ');
                }
                cout << "
    ";
            }
    
                
        }
        return 0;
    }

    效果

  • 相关阅读:
    Informix IDS 11零碎规画(918考试)认证指南,第 7 部分: IDS复制(15)
    Informix IDS 11琐屑管理(918考试)认证指南,第 7 局部: IDS复制(10)
    近期招聘
    Classes 单元下的公用函数目录
    Graphics 单元下的公用函数目录
    CnPack 使用的组件命名约定
    Windows 单元下的公用函数目录(RZ_)
    Variants 单元下的公用函数目录
    StrUtils 单元下的公用函数目录
    Math 单元下的公用函数目录
  • 原文地址:https://www.cnblogs.com/ncgds/p/7448787.html
Copyright © 2011-2022 走看看