算法竞赛入门经典_5 c++与STL入门

来源:互联网 发布:unity3d js教程 编辑:程序博客网 时间:2024/06/03 17:13
 

直接跳到第五章了

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

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

#include<cstdio>int main(){    int a, b;    while(scanf("%d%d", &a, &b) == 2)        printf("%d\n", 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:\n", ++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\n", x, p+1);                else printf("%d not found\n", 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("\n");        }    }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\n", ++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\n", 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("\n");    }    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时补字符extravoid 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 << "\n";        }                }    return 0;}

效果

 

原创粉丝点击