算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题(9/12)

来源:互联网 发布:神奇百货ceo骗局 知乎 编辑:程序博客网 时间:2024/04/30 00:27

说明

本文是我对第五章12道例题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。
另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第五章例题contest
如果想直接看某道题,请点开目录后点开相应的题目!!!

例题

例5-1 UVA 10474 大理石在哪儿 (排序和查找)

思路
这个题比较基础,排序用sort,查找用lower_bound,都是STL的标准函数。注意lower_bound函数的返回值是不小于查找数的第一个位置对应的指针,找不到则返回a+n(也就是函数的第二个参数)。
代码

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int N = 10000;int main(void){    int n, q, t = 0;    int a[N], x;    while (scanf("%d%d", &n, &q) != EOF && n) {        for (int i = 0; i < n; i ++)            scanf("%d", &a[i]);        sort(a, a+n);        printf("CASE# %d:\n", ++t);        while (q--) {            scanf("%d", &x);            int m = lower_bound(a, a+n, x) - a;            printf("%d ", x);            if (m == n || a[m] != x) printf("not found\n");            else printf("found at %d\n", m+1);        }    }    return 0;}

例5-2 UVA 101 木块问题 (using std::vector)

思路
由于此题中木块堆的长度一直在变化,并需要频繁的添加和删除元素操作,用vector是最合适的。
注意学习vector的功能函数用法,如size() resize() push_back()等。
另外,这个题的四种操作中具体操作有一定的重复,合理拆分操作能够使代码模块化更好。
代码

#include <iostream>#include <cstdio>#include <algorithm>#include <vector>using namespace std;const int N = 25;typedef pair<int, int> P;int n;vector<int> pile[N];P find_pile(int a){    for (int i = 0; i < n; i ++) {        for (int j = 0; j < pile[i].size(); j ++) {            if (pile[i][j] == a) return P(i, j);        }    }    return P(n, n);}void clear_above(P p){    int i = p.first, j = p.second;    for (int k = j+1; k < pile[i].size(); k ++) {        int a = pile[i][k];        pile[a].push_back(a);    }    pile[i].resize(j+1);}void pile_over(P p, int b){    int i = p.first, j = p.second;    for (int k = j; k < pile[i].size(); k ++) {        int a = pile[i][k];        pile[b].push_back(a);    }    pile[i].resize(j);}void print(){    for (int i = 0; i < n; i ++) {        printf("%d:", i);        for (int j = 0; j < pile[i].size(); j ++) {            printf(" %d", pile[i][j]);        }        printf("\n");    }}int main(void){    cin >> n;    for (int i = 0; i < n; i ++) pile[i].push_back(i);    string s1, s2;    int a, b;    P p1, p2;    while (cin >> s1 && s1 != "quit") {        cin >> a >> s2 >> b;        p1 = find_pile(a);        p2 = find_pile(b);        if (p1.first == p2.first) continue;        if (s1 == "move") clear_above(p1);        if (s2 == "onto") clear_above(p2);        pile_over(p1, p2.first);        //print();        //printf("===============\n");    }    print();    return 0;}

例5-3 UVA 10815 安迪的第一本字典 (using std::set)

思路
此题中涉及的知识点有:
1、set的用法(注意set中没有重复的值)
2、isalpha isdigit等函数的用法
3、迭代器iterator的用法
注意学习。
代码

#include <cstdio>#include <iostream>#include <string>#include <cctype>#include <set>using namespace std;set<string> d;int main() {    string s;                                                                              char ch;             while (getline(cin, s)) {                                                                  for (int i = 0; i < s.size(); i++) {            if (!isalpha(s[i]))                                                                        continue;                            string tmp;                                                                            while (i < s.size() && isalpha(s[i]))                tmp += tolower(s[i++]);                                                            d.insert(tmp);                               }    }                                                                                      for (set<string>::iterator i = d.begin(); i != d.end(); i++)        cout << *i << endl;                                                                return 0;                                                    }

例5-4 UVA 156 反片语 (using std::map)

思路
我的代码用了两个map,不如书中的例程代码用了一个map和一个vector。
代码

#include <iostream>#include <algorithm>#include <string>#include <map>#include <set>using namespace std;map<string, string> sorted;map<string, int> num;int main(void){    string s, s1;    char ch[21];    while (cin >> s && s != "#") {        int n = s.size();        for (int i = 0; i < s.size(); i ++)            ch[i] = tolower(s[i]);        ch[n] = '\0';        sort(ch, ch+n);        s1 = ch;        sorted[s] = s1;        if (num.find(s1) == num.end()) num[s1] = 1;        else num[s1] ++;    }    set<string> res;    for (map<string, string>::iterator it = sorted.begin();        it != sorted.end(); it ++) {        if (num[it->second] == 1) res.insert(it->first);    }    for (set<string>::iterator it = res.begin();        it != res.end(); it ++) {        cout << *it << endl;    }    return 0;}

例5-5 UVA 12096 集合栈计算机 (using std::stack and other containers)

思路
这个题主要使用的数据结构是stack,但最重要的知识点却不是stack!
本题有一种用映射简化数据结构的思想:将另一种相对复杂的数据结构用map映射成int型id,不仅能够大大降低存储空间,而且也可以大大提高查找效率。
因此这是第五章最重要的一道题,没有之一!!!
后续习题或例题中有好几道用到了这种思想,后面会讲到。
代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<set>#include<map>#include<vector>#include<stack>using namespace std;typedef set<int> Set;map<Set, int> IDs;vector<Set> Sets;stack<int> st;int getID(Set s){    if (IDs.count(s)) return IDs[s];    Sets.push_back(s);    return IDs[s] = Sets.size()-1;}void init(){    Set s0;    getID(s0);}void push(){    st.push(0);}int main(){    int t, n;    scanf("%d", &t);    while (t--) {        scanf("%d", &n);        init();        char op[10];        Set s1, s2, s3;        int id;        while (n--) {            scanf("%s", op);            switch(op[0]) {                case 'P':                    st.push(0);                    break;                case 'D':                    st.push(st.top());                    break;                case 'U':                    s1 = Sets[st.top()]; st.pop();                    s2 = Sets[st.top()]; st.pop();                    for (Set::iterator it = s2.begin(); it != s2.end(); it++)                      s1.insert(*it);                    st.push(getID(s1));                    break;                case 'I':                    s1 = Sets[st.top()]; st.pop();                    s2 = Sets[st.top()]; st.pop();                    s3.clear();                    for (Set::iterator it = s2.begin(); it != s2.end(); it++)                      if (s1.count(*it)) s3.insert(*it);                    st.push(getID(s3));                    break;                case 'A':                    id = st.top(); st.pop();                    s2 = Sets[st.top()]; st.pop();                    s2.insert(id);                    st.push(getID(s2));                    break;                default:                    break;            }            printf("%d\n", Sets[st.top()].size());        }        printf("***\n");    }    return 0;}

例5-6 UVA 540 团体队列 (using std::queue and other containers)

思路
队列的特点是先入先出,通常排队系统的题会用到队列。
本题用两个队列解决:每个团体有一个队列(元素为团体成员ID),团队整体又形成一个队列(元素为团体ID)。
代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <map>#include <queue>using namespace std;const int T = 1000;int t;map<int, int> his_team;queue<int> qt, q[T];void init_queue(){    while (qt.size()) qt.pop();    for (int i = 0; i < t; i++)        while (q[i].size()) q[i].pop();}int main(void){    int kase = 0, n, m;    char op[10];    while (scanf("%d", &t) && t) {        for (int i = 0; i < t; i++) {            scanf("%d", &n);            for (int j = 0; j < n; j++) {                scanf("%d", &m);                his_team[m] = i;            }        }        init_queue();        printf("Scenario #%d\n", ++kase);        while (scanf("%s", op) && strcmp(op, "STOP")) {            if (op[0] == 'E') {                scanf("%d", &m);                int i = his_team[m];                if (q[i].empty()) qt.push(i);                q[i].push(m);            } else {                int i = qt.front();                printf("%d\n", q[i].front());                q[i].pop();                if (q[i].empty()) qt.pop();            }        }        printf("\n");    }    return 0;}

例5-7 UVA 136 丑数 (using priority_queue)

思路
这个题的uva上一直submit error,但我程序的运行结果与其他博客对比过,完全正确。
此题没有必要一定用long long,因为所求结果(第1500个丑数)并不超过int表示范围。但我结果*5却会超过,所以我的程序中用了long long。
代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;typedef long long LL;int main(){    LL a = 0, b = 0;    priority_queue<LL, vector<LL>, greater<LL> > pq;    pq.push(1);    int cnt = 0;    while (pq.size()) {        a = pq.top();        pq.pop();        if (a != b) {            cnt++;            if (cnt == 1500) break;            pq.push(a*2);            pq.push(a*3);            pq.push(a*5);            b = a;        }    }    printf("%lld\n", a);    return 0;}

例5-8 UVA 400 Unix is 命令 (排序和字符串处理)

思路
注意这个题要求的是按列优先方式输出,这与程序正常输出的方向不同。因此需要改变循环层次,具体见程序。
另外只要有字符后面就要补全M或M+2个字符,如果没有字符则不用。
代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int N = 100;const int M = 60;void print_char(int k, char c){    while (k--) printf("%c", c);}int main(){    int n, m, col, row;    string s[N];    while (~scanf("%d", &n)) {        m = 0;        for (int i = 0; i < n; i++) {          cin >> s[i];          m = max(m, (int)s[i].size());        }        sort(s, s+n);        col = (M-m)/(m+2) + 1;        row = n/col + ((n%col) ? 1 : 0);        print_char(M, '-');        printf("\n");        for (int j = 0; j < row; j ++) {            for (int i = 0; i < col; i ++) {                int k = i*row+j;                if (i == col-1) {                    if (k < n) {                        cout << s[k];                        print_char(m-s[k].size(), ' ');                    }                    printf("\n");                } else {                    cout << s[k];                    print_char(m+2-s[k].size(), ' ');                }            }        }    }    return 0;}

例5-9 UVA 1592 数据库 (uniquely using std::map)

思路
四重循环枚举肯定会超时,实际上存在大量无用的比较。实际上只需要枚举c1和c2,然后从上到下扫描各行。每次碰到一个新的行r,把c1,c2两列的内容作为一个二元组存到一个map中。如果map的键值中已经存在这个二元组,该二元组映射到的就是所要求的r1,而当前行就是r2。
还有一个细节问题:如何表示由c1,c2两列组成的二元组?一种方法是直接用两个字符串拼成一个长字符串,速度更快的则是进行预处理——给所有字符串分配一个编号,则整个数据库中每个单元格都变成了整数,上述二元组就变成了两个整数。这是例子5-5中介绍的技巧。
另外在做的时候有一个小插曲,第一次提交TLE了,查了一下程序发现每组数据计算时忘记重新对map进行初始化。
代码

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<map>#include<vector>using namespace std;const int N = 10000;const int M = 10;typedef pair<int, int> P;map<P, int> firstpos;map<string, int> str2key;vector<string> key2str;int ID(string &s){    if (str2key.count(s)) return str2key[s];    key2str.push_back(s);    return str2key[s] = key2str.size()-1;}int main(){    int r, c;    char ch;    string tab[N][M];    while (cin >> r >> c) {        getchar();        for (int i = 0; i < r; i++) {            for (int j = 0; j < c; j++) {                tab[i][j] = "";                while ((ch = getchar()) != ',' && ch != '\n')                  tab[i][j] += ch;            }        }        int flag = true;        for (int j = 0; j < c; j++) {            for (int k = j+1; k < c; k++) {                firstpos.clear();                str2key.clear();                key2str.clear();                for (int i = 0; i < r; i++) {                    P p(ID(tab[i][j]), ID(tab[i][k]));                    if (firstpos.count(p)) {//find                        printf("NO\n");                        printf("%d %d\n", firstpos[p]+1, i+1);                        printf("%d %d\n", j+1, k+1);                        flag = false;                        break;                    } else                        firstpos[p] = i;                }                if (flag == false) break;            }            if (flag == false) break;        }        if (flag) printf("YES\n");    }    return 0;}

例5-10 UVA 207 PGA 巡回赛的奖金 (排序和其他细节处理)

思路
该题尚未尝试,先占个位置。
代码


例5-11 UVA 814 邮件传输代理的交互 (字符串以及 STL 容器综合运用)

思路
该题尚未尝试,先占个位置。
代码


例5-12 UVA 221 城市正视图 (离散化)

思路
该题尚未尝试,先占个位置。
代码


0 0
原创粉丝点击