STL专题(入门)

本文介绍了多个算法和数据结构的实现与应用,包括字符串反转、全排列、队列模拟、栈操作、字典操作、集合操作、优先队列等。通过实例展示了如何运用这些基础知识解决实际问题,如字符串处理、数组排序、集合查找等。文章旨在提升读者的算法思维和编程能力。
摘要由CSDN通过智能技术生成

01. HDU1062 Text Reverse

  • 思路:对于每一行读入后,在遇到空格或换行前都压入栈,有空格就全部倒序压出,再输出一个空格;有换行就倒序输出再输出一个换行即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e4 + 5;
    const db eps = 1e-10;
    using namespace std;
    int cas;
    string s;
    char c[N];
    stack<char> st;
    int main(){
        cin >> cas;
        getchar();
        while(cas--){
            gets(c);
            for(int i = 0; i <= strlen(c); i++){
                if(c[i] == ' ' || i == strlen(c)){  //记住当i == strlen()时也是输出点!!!
                    while(st.size()){
                        cout << st.top();
                        st.pop();
                    }
                    if(c[i] == ' ') cout << " ";  //PE警告:结尾不能加空格,只有c[i]为空格时才加
                }
                else st.push(c[i]);
            }
            cout << endl;
        }
    }
    

02. NOIP2004 火星人

  • 思路:运用计算全排列的next_permutation函数,向后数m次即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e4 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, m, a[N];
    int main(){
        cin >> n >> m;
        rep(i, 1, n) cin >> a[i];
        //next_permutation全排列下一个, prev_permutation全排列上一个
        while(m--) next_permutation(a + 1, a + 1 + n);
        rep(i, 1, n) cout << a[i] << " ";
    }
    

03. POJ2259 Team Queue

  • 思路:要模拟一个team queue,可以对于每组team设计一个属于其自己的队列,储存本组被选的数;再用一个队列q[0]储存各组之间的顺序。这样插入一个数时,如果其组有数,直接压入到其组队列的末尾,否在在队列q[0]中先压入组号,新建其组队列压入即可。查询弹出时只需要在q[0]中组号排第一的组中找队头即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e3 + 5;
    const db eps = 1e-10;
    using namespace std;
    int t, team[1000005], cnt = 0, teamnum, x, n;
    char s[10];
    int main(){
        while(cin >> t, t){
            teamnum = 0;
            rep(i, 1, t){
                teamnum++;
                cin >> n;
                while(n--){
                    scanf("%d", &x);
                    team[x] = teamnum;  //记录 x 对应的队伍编号
                }
            }
            printf("Scenario #%d\n", ++cnt);
            queue<int> q[N];
            while(1){
                scanf("%s", s);  //string 字符串只能用 cin 读入(本题用时296ms)。char数组 用scanf读入不能加 & 符号(本题用时78ms)!!!
                if(s[0] == 'S') break;
                else if(s[0] == 'E'){  //排队的时候并不用将所有数排到一起,只需要记录每个 team 自己[入选的数]和 team 之间的[顺序]即可
                    scanf("%d", &x);
                    int tmp = team[x];
                    if(!q[tmp].size()) q[0].push(tmp);  //q[0] 是存队伍编号的队列
                    q[tmp].push(x);  //q[tmp] 是存编号为 tmp 的数字的队列
                }
                else{
                    int tmp = q[0].front();
                    printf("%d\n", q[tmp].front());
                    q[tmp].pop();
                    if(!q[tmp].size()) q[0].pop();
                }
            }
            cout << endl;
        }
    }
    

04. UVA101 The Blocks Problem

  • 思路:对于四种操作,发现只需要三种情况排列组合选择即可。于是写三种函数分别表示将所有上方物品放回原位、将x放到y栈顶、将x及上方物品按顺序放到y栈顶。对于每一个位置都设计一个不定长数组,对于每一个物品都用address记录其当前位置。对于三种操作分别调整即可。要注意最后输出时每行末尾不能有空格。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e6 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, a, b, address[30];
    string command, s;
    vector<int> vec[30];
    void Back(int x){  //将 x 上方物品放回原位
        int stx = address[x], len;
        vector<int> &v = vec[stx];
        len = v.size() - 1;
        while(v[len] != x){
            int tmp = v[len];
            v.pop_back(), vec[tmp].push_back(tmp);
            address[tmp] = tmp;
            len--;
        }
    }
    void onlygo(int x, int y){  //只将 x 放到 y 的栈顶上方
        vec[address[x]].pop_back(), vec[address[y]].push_back(x);
        address[x] = address[y];
    }
    void allgo(int x, int y){  //将 x 及上方物品放到 y 的栈顶上方
        int stx = address[x], sty = address[y], lenx;
        vector<int> &v = vec[stx];
        stack<int> stmp;
        lenx = v.size() - 1;
        while(v[lenx] != x){
            stmp.push(v[lenx]), v.pop_back();
            lenx--;
        }
        stmp.push(v[lenx]), v.pop_back();
        while(stmp.size()){
            int tmp = stmp.top();
            stmp.pop();
            vec[sty].push_back(tmp);
            address[tmp] = sty;
        }
    }
    int main(){
        cin >> n;
        rep(i, 0, n - 1){
            vec[i].push_back(i);
            address[i] = i;
        }
        while(1){
            cin >> command;
            if(command == "quit") break;
            cin >> a >> s >> b;
            if(address[a] == address[b]) continue;  //非法命令
            if(command == "move"){
                if(s == "onto"){  //a, b 上方均回原位,a到b上
                    Back(a), Back(b);
                    onlygo(a, b);
                }
                else{  //a 上方回原位,a 到 b 的栈顶上
                    Back(a);
                    onlygo(a, b);
                }
            }
            else{
                if(s == "onto"){  //b 上方回原位,a 及上方到 b 上
                    Back(b);
                    allgo(a, b);
                }
                else{  //a 及上方到 b 的栈顶上
                    allgo(a, b);
                }
            }
        }
        rep(i, 0, n - 1){
            printf("%d:", i);
            int sz = vec[i].size();
            if(sz) rep(j, 0, sz - 1) printf(" %d", vec[i][j]);
            printf("\n");
        }
    }
    

05. UVA156 Ananagrams

  • 思路:要查找所有不是反片语的词,那我们就要统计一下那些重复出现了。为了调整格式,使其统一,我们全部转为小写后将每个词内部按字符排序,再压入map自动排序所有词,若有重复就记录一下。最后统计所有只有一次的词找到其原词。将这些选出词排序后输出即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e2 + 5;
    const db eps = 1e-10;
    using namespace std;
    int cnt = 0;
    string s[N], t[N], ans[N];
    // mp->first表示化为小写且内部排序后的字符串
    // mp->second.first表示此字符串出现的次数
    // mp->second.second表示此字符串对应 s 时的位置
    map<string, pair<int, int> > mp;
    void Lower_Sort(string &x){
        int ls = x.size();
        rep(i, 0, ls - 1) 
            if('A' <= x[i] && x[i] <= 'Z') x[i] += 32;
        sort(x.begin(), x.end());
    }
    int main(){
        while(cin >> s[++cnt], s[cnt] != "#") t[cnt] = s[cnt];
        cnt--;
        rep(i, 1, cnt){
            Lower_Sort(t[i]);
            if(mp.count(t[i])) mp[t[i]].first++;
            else mp.insert(make_pair(t[i], make_pair(1, i)));
        }
        map<string, pair<int, int> >::iterator iter;
        int num = 0;
        for(iter = mp.begin(); iter != mp.end(); iter++){
            pair<int, int> tmp = iter->second;
            if(tmp.first == 1) ans[++num] = s[tmp.second];  //记录选择的字符串
        }
        sort(ans + 1, ans + 1 + num);  //按照原字符串字典序排序
        rep(i, 1, num) cout << ans[i] << endl;
    }
    

06. UVA12096 The SetStack Computer

  • 思路:对于任意一个不同集合,我们都尝试用一个数值id记录他,用map储存便于判断是否存在,用vector储存集合编号便于查询。题目中的UNION和INTERSECT操作都可以用set自带并集、交集函数实现。在最终总栈中,根据每次操作,不断改变栈内元素即可。则询问的栈顶元素大小,就可以根据vector中的编号查到此集合,再求大小即可。

    //练习set, stack, map
    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define all(x) x.begin(), x.end() 
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e6 + 5;
    const db eps = 1e-10;
    using namespace std;
    int cas, n;
    char s[10];
    map<set<int>, int> mp;  //记录是否有过此集合的map
    vector<set<int> > vec;  //储存每个集合的编号
    stack<int> st;  //存集合的栈
    int ID(set<int> x){
        if(!mp.count(x)){
            vec.push_back(x);
            mp[x] = vec.size() - 1;
        }
        return mp[x];
    }
    int main(){
        cin >> cas;
        while(cas--){
            cin >> n;
            while(n--){
                scanf("%s", s);
                if(s[0] == 'P') st.push(ID(set<int> ()));
                else if(s[0] == 'D') st.push(st.top());
                else{
                    set<int> x1 = vec[st.top()]; st.pop();
                    set<int> x2 = vec[st.top()], tmp; st.pop();
                    if(s[0] == 'U') set_union(all(x1), all(x2), inserter(tmp, tmp.begin()));
                    else if(s[0] == 'I') set_intersection(all(x1), all(x2), inserter(tmp, tmp.begin()));
                    else{
                        tmp = x2;
                        tmp.insert(ID(x1));
                    }
                    st.push(ID(tmp));
                }
                cout << vec[st.top()].size() << endl;  //栈顶编号对应集合的大小
            }
            cout << "***" << endl;
        }
    }
    

07. UVA1618 Weak Key

  • 思路:这道题有点难,要充分利用不定长数组中的各种复杂度为O(logn)的查询。用两个数组h[i],l[i]分别记录每个数i后面大于、小于a[i]的位置j。比如对于第一个要求p<q<r<s,Nq>Ns>Np>Nr,遍历每个p,在h[p]中遍历每个q,在l[p]中找第一个大于q的数r,若r存在再在h[p]中找第一个大于r的s,若s存在再判断其是否能l[q]中,若存在便说明已找到。第二个要求同理,只是顺序相反。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define all(x) x.begin(), x.end() 
    #define VI vector<int>
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 5e3 + 5;
    const db eps = 1e-10;
    using namespace std;
    int cas, n, a[N];
    VI h[N], l[N];  //h[i]存(j > i && a[j] > a[i]),l[i]存(j > i && a[j] < a[i])
    bool find1(int p){  //找p < q < r < s && a[q] > a[s] > a[p] > a[r]
        if(h[p].empty()) return 0;
        rep(qnum, 0, h[p].size() - 1){  //从小到大先找 q, 再找 r, 最后找 s
            int q = h[p][qnum];
            VI::iterator rpos = upper_bound(all(l[p]), q);  //找 a[r] < a[p] 的第一个 r > q 的 r.用迭代器在 l[p] 中找
            if(rpos == l[p].end()) continue;  //没有
            int r = *rpos;
            VI::iterator spos = upper_bound(all(h[p]), r);
            while(spos != h[p].end()){
                int s = *spos;
                spos++;
                if(binary_search(all(l[q]), s)) return 1;
            }
        }
        return 0;
    }
    bool find2(int p){  //找p < q < r < s && a[q] < a[s] < a[p] < a[r]
        if(l[p].empty()) return 0;
        rep(qnum, 0, l[p].size() - 1){
            int q = l[p][qnum];
            VI::iterator rpos = upper_bound(all(h[p]), q);  //找 a[r] > a[p] 的第一个 r > q 的 r
            if(rpos == h[p].end()) continue;  //没有
            int r = *rpos;
            VI::iterator spos = upper_bound(all(l[p]), r);
            while(spos != l[p].end()){
                int s = *spos;
                spos++;
                if(binary_search(all(h[q]), s)) return 1;
            }
        }
        return 0;
    }
    bool solve(){
        rep(i, 1, n) if((find1(i) || find2(i))) return 1;
        return 0;
    }
    int main(){
        cin >> cas;
        while(cas--){
            cin >> n;
            rep(i, 1, n){
                scanf("%d", &a[i]);
                h[i].clear(), l[i].clear();
            }
            rep(i, 1, n) rep(j, i + 1, n){  //预处理
                if(a[j] > a[i]) h[i].push_back(j);
                else if(a[j] < a[i]) l[i].push_back(j);
            }
            puts(solve() ? "YES" : "NO");
        }
    }
    

08. UVA10474 Where is the Marble

  • 思路:对于每个数字,在排序后的不定长数组中找第一个大于等于要查询的数,若找到一致便输出,否则没有。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e6 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, q, a[N], cnt = 0;
    vector<int> vec;
    int main(){
        while(cin >> n >> q, n || q){
            vec.clear();
            rep(i, 1, n){
                int t; scanf("%d", &t);
                vec.push_back(t);
            }
            sort(vec.begin(), vec.end());
            printf("CASE# %d:\n", ++cnt);
            rep(i, 1, q){
                int t; scanf("%d", &t);
                int v = lower_bound(vec.begin(), vec.end(), t) - vec.begin();
                if(vec[v] != t) printf("%d not found\n", t);
                else printf("%d found at %d\n", t, v + 1);
            }
        }
    }
    

09. NOIP2004 合并果子

  • 思路:用优先队列每个找前两个最小的堆合并即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e4 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, a[N];
    ll ans;
    priority_queue <int, vector<int>, greater<int> > q;
    int main(){
        cin >> n;
        rep(i, 1, n){
            cin >> a[i];
            q.push(a[i]);
        }
        rep(i, 1, n - 1){
            int first = q.top(); q.pop();
            int second = q.top(); q.pop();
            ans += first + second;
            q.push(first + second);
        }
        cout << ans << endl;
    }
    

10. POJ1589 Unix Is

  • 思路:这道题重点是算出格子行列数和精确宽度,确定每列剩余宽度后填补空位即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e2 + 5;
    const db eps = 1e-10;
    using namespace std;
    int n, maxn, row, col;
    string s[N];
    int main(){
        while(cin >> n){
            maxn = 0;
            rep(i, 1, n){
                cin >> s[i];
                maxn = max(maxn, (int)s[i].length());
            }
            col = (60 - maxn) / (maxn + 2) + 1;  //计算col
            row = n / col + (n % col == 0 ? 0 : 1);  //计算row
            sort(s + 1, s + 1 + n);
            rep(i, 1, 60) cout << '-';
            cout << endl;
            rep(i, 1, row){
                rep(j, 0, col - 1){
                    int num = i + j * row, rest = maxn + (j == col - 1 ? 0 : 2) - (int)s[num].length();  //注意rest计算很容易PE
                    if(num <= n){
                        cout << s[num];
                        rep(i, 1, rest) cout << " ";
                    }
                }
                cout << endl;
            }
        }
    }
    

12. UVA10815 Andy’s First Dictionary

  • 思路:对于读入的字符串,要把所有标点改成空格,再用流stringstream 重新读入一遍,将刷新空格后的字符串存入集合避免重复。最后按顺序输出即可。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e4 + 5;
    const db eps = 1e-10;
    using namespace std;
    string s;
    set<string> st;  //set自带排序
    int main(){
        while(cin >> s){
            rep(i, 0, s.size() - 1){
                if(!isalpha(s[i])) s[i] = ' ';
                else s[i] = tolower(s[i]);
            }
            stringstream tmp(s);  //流的输入输出: 将字符串作为完整读入流中,再输出
            while(tmp >> s) st.insert(s);
        }
        for(auto i: st) cout << i << endl;
    }
    

13. POJ1338 Ugly Numbers

  • 思路:用一个集合判断每次压入数字是否重复,用一个优先队列提取每次最小的数查询下一个是 * 2、* 3还是*5即可。最后枚举到第1500个即可。

    //优先队列
    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 2e3;
    const db eps = 1e-10;
    using namespace std;
    ll tmp, a[N];
    set<int> flg;
    priority_queue<ll, vector<ll>, greater<ll> > q;
    int main(){
        q.push(1), flg.insert(1);
        rep(i, 1, 1500){
            tmp = a[i] = q.top();  //此时tmp一定是第i小的
            q.pop();
            if(!flg.count(tmp * 2))  //判断这个数之前是否存过
                flg.insert(tmp * 2), q.push(tmp * 2);
            if(!flg.count(tmp * 3))
                flg.insert(tmp * 3), q.push(tmp * 3);
            if(!flg.count(tmp * 5))
                flg.insert(tmp * 5), q.push(tmp * 5);
        }
        printf("The 1500'th ugly number is %d.", a[1500]);  //859963392
    }
    

14. UVA1595 Symmetry

  • 思路:由于只需要找竖线,那可以在读入后算出所有x轴的平均值。遍历每一个点,查看是否有关于此竖线对称的点存在,若没有直接break即可,小数需注意精度问题。

    #include<bits/stdc++.h>
    #define rep(i, a, b) for(int i = a; i <= b; i++)
    #define per(i, a, b) for(int i = a; i >= b; i--)
    #define ll long long
    #define db double
    #define PII pair<int, int>
    #define pi acos(-1.0)
    const int INF = 0x7fffffff;
    const int N = 1e3 + 5;
    const db eps = 1e-10;
    using namespace std;
    int cas, n, x, y;
    bool flg;
    db avg;
    PII a[N];
    set<PII> st;
    bool cmp(PII a, PII b){
        if(a.first == b.first) return a.second < b.second;
        return a.first < b.first;
    }
    int main(){
        cin >> cas;
        while(cas--){
            cin >> n;
            st.clear();
            avg = 0;
            rep(i, 1, n){
                int x1, y1; 
                scanf("%d%d", &x1, &y1);
                avg += (db)x1;
                a[i] = make_pair(x1, y1), st.insert(a[i]);
            }
            avg /= (db)n, flg = 1;  //若有对称轴,一定是平均值
            rep(i, 1, n){
                x = a[i].first, y = a[i].second;
                if(fabs(x - avg) <= eps) continue;
                if(!st.count(make_pair(2 * avg - x, y))){  //在集合中找是否有对应点
                    flg = 0;
                    break;
                }
            }
            puts(flg ? "YES" : "NO");
        }
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值