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

说明

本文是我对第五章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 城市正视图 (离散化)

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

你可能感兴趣的:(算法,ACM,STL,uva,算法竞赛入门经典)