zoukankan      html  css  js  c++  java
  • [ACM]Two Point & 尺取 & 离散化 & C++STL( struct重写,容器应用 )

    Two Point & 尺取 & 离散化 & C++STL( struct重写,容器应用 )

    1____C++ STL

    1.1____什么是STL


    我们先来看看一个C++代码

    #include <iostream>
    #include <iomanip>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    struct Point
    {
        double x,y;
    
        Point(double _x,double _y){
            x = _x,y = _y;
        }
    
        Point(){}
    };
    
    int main()
    {
        vector<Point> vec;
    
        vec.push_back({1,2});
        vec.push_back(Point(3,4.34));
        vec.push_back({1,231});
        vec.push_back({2131,2});
        vec.push_back({12341,213});
    
        //vec.push_back(make_pair(2,3));
    
    
        ///读入
    
        double a,b;
        cin >>a>> b;
        vec.push_back(Point(a,b));
    
    
        for(vector<Point>::iterator it = vec.begin(); it != vec.end() ; it ++){
            cout << (*it).x << " " << (*it).y << endl;
        }
    
        cout << endl;
        cout.setf(ios::fixed);
    
        reverse(vec.begin(),vec.end());
        //reverse(a,a+n);
    
    
        for(auto it : vec){
            cout << setprecision(2)  << it.x << "-" << it.y << endl;
        }
    
        return 0;
    }
    
    

    C++标准

    ​ 首先需要介绍的是 C++ 本身的版本。由于 C++ 本身只是一门语言,而不同的编译器对 C++ 的实现方法各不一致,因此需要标准化约束编译器的实现,使得 C++ 代码在不同的编译器下表现一致。

    标准模板库 STL

    ​ 标准模板库(英文Standard Template Library缩写STL),是一个C++软件库,大量影响了C++标准程序库但并非是其的一部分。其中包含4个组件,分别为算法容器函数迭代器

    模板是C++程序设计语言中的一个重要特征,而标准模板库正是基于此特征。标准模板库使得C++编程语言在有了同Java一样强大的类库的同时,保有了更大的可扩展性

    image-20210717141116065

    STL在数据上执行的操作与要执行操作的数据分开,分别以如下概念指代:

    • 容器:包含、放置数据的地方。
    • 迭代器:在容器中指出一个位置、或成对使用以划定一个区域,用来限定操作所涉及到的数据范围。
    • 算法:要执行的操作。

    函数:

    • lower_bound
    • upper_bound
    • get_maxelement
    • ...

    扩展

    ​ getchar() 与 cin.get()

    1.2____容器


    ​ 在计算机科学中,容器以一种遵循特定访问规则的系统的方法来存储对象。

    思考:我们之前的存储对象有些什么? 数组, 单个元素。

    image-20210717140310667

    img

    1.2.1____vector

    std::vector 是 STL 提供的 内存连续的可变长度 的数组(亦称列表)数据结构。能够提供线性复杂度的插入和删除,以及常数复杂度的随机访问。

    1. 为什么使用vector?

      • vector 重写了比较运算符和赋值运算符
      • vector 的可变长
      • vector的内存是动态分配
    2. 元素访问

      vector 提供了如下几种方法进行元素访问

      • at()

      v.at(pos) 返回容器中下标为 pos 的引用。如果数组越界抛出 std::out_of_range 类型的异常。

      • operator[]

      v[pos] 返回容器中下标为 pos 的引用。不执行越界检查。

      • front()

      v.front() 返回首元素的引用。

      • back()

      v.back() 返回末尾元素的引用。

      • data()

      v.data() 返回指向数组第一个元素的指针。

    3. vector中的迭代器

      image-20210717151716096

    4. 与长度有关的 函数

      • empty() 返回一个 bool 值,即 v.begin() == v.end()true 为空,false 为非空。
      • size() 返回容器长度(元素数量),即 std::distance(v.begin(), v.end())
    5. 元素的删除及修改
      - clear() 清除所有元素
      - insert() 支持在某个迭代器位置插入元素、可以插入多个。复杂度与 pos 距离末尾长度成线性而非常数的
      - erase() 删除某个迭代器或者区间的元素,返回最后被删除的迭代器。复杂度与 insert 一致。
      - push_back() 在末尾插入一个元素,均摊复杂度为 常数,最坏为线性复杂度。
      - pop_back() 删除末尾元素,常数复杂度。

    1.2.2____deque

    ​ 基本上与vector一直,唯一需要注意的是,在队列的首,尾插入删除元素为常数级

    1.2.3____set

    set 是关联容器,含有键值类型对象的已排序集搜索移除插入拥有对数复杂度(nlog(n)))。set 内部通常采用红黑树实现。平衡二叉树的特性使得 set 非常适合处理需要同时兼顾查找、插入与删除的情况。

    1. 插入与删除

      • insert(x) 当容器中没有等价元素的时候,将元素 x 插入到 set 中。
      • erase(x) 删除值为 x 的 所有 元素,返回删除元素的个数。( 与后面的multiset 有区别)
      • erase(pos) 删除迭代器为 pos 的元素,要求迭代器必须合法。
      • erase(first,last) 删除迭代器在 范围内的所有元素。
      • clear() 清空 set
    2. 查询操作

      • count(x) 返回 set为 x 的元素数量。
      • find(x)set 内存在为 x 的元素时会返回该元素的迭代器,否则返回 end()
      • lower_bound(x) 返回指向首个不小于给定键的元素的迭代器。如果不存在这样 的元素,返回 end()
      • upper_bound(x) 返回指向首个大于给定键的元素的迭代器。如果不存在这样的元素,返回 end()
      • empty() 返回容器是否为空。
      • size() 返回容器内元素个数。

    image-20210717153952738

    1. set 在贪心中使用

      在贪心算法中经常会需要出现类似 找出并删除最小的大于等于某个值的元素。这种操作能轻松地通过 set 来完成。

      // 现存可用的元素
      set<int> se;
      // 需要大于等于的值
      int x;
      
      // 查找最小的大于等于x的元素
      set<int>::iterator it = se.lower_bound(x);
      if (it == se.end()) {
        // 不存在这样的元素,则进行相应操作……
      } else {
        // 找到了这样的元素,将其从现存可用元素中移除
        se.erase(it);
        // 进行相应操作……
      }
      

    例1

    Hyperset - CodeForces 1287B


    Bees Alice and Alesya gave beekeeper Polina famous card game "Set" as a Christmas present. The deck consists of cards that vary in four features across three options for each kind of feature: number of shapes, shape, shading, and color. In this game, some combinations of three cards are said to make up a set. For every feature — color, number, shape, and shading — the three cards must display that feature as either all the same, or pairwise different. The picture below shows how sets look.

    img

    Polina came up with a new game called "Hyperset". In her game, there are nn cards with kk features, each feature has three possible values: "S", "E", or "T". The original "Set" game can be viewed as "Hyperset" with $ k=4$.

    Similarly to the original game, three cards form a set, if all features are the same for all cards or are pairwise different. The goal of the game is to compute the number of ways to choose three cards that form a set.

    Unfortunately, winter holidays have come to an end, and it's time for Polina to go to school. Help Polina find the number of sets among the cards lying on the table.

    Input

    The first line of each test contains two integers nn and kk ((1≤n≤1500), (1≤k≤30) — number of cards and number of features.

    Each of the following nn lines contains a card description: a string consisting of kk letters "S", "E", "T". The ii-th character of this string decribes the ii-th feature of that card. All cards are distinct.

    Output

    Output a single integer — the number of ways to choose three cards that form a set.

    Examples

    input

    3 3
    SET
    ETS
    TSE
    

    output

    1
    

    input

    3 4
    SETE
    ETSE
    TSES
    

    output

    0
    

    input

    5 4
    SETT
    TEST
    EEET
    ESTE
    STES
    

    output

    2
    

    Note

    In the third example test, these two triples of cards are sets:

    1. "SETT", "TEST", "EEET"
    2. "TEST", "ESTE", "STES"

    1.2.4____map

    1. 什么是键值对

      在将后者与前者结合起来就是map了

      image-20210717155158082

    2. map

      map 是有序键值对容器,它的元素的键是唯一的。搜索、移除和插入操作拥有对数复杂度map 通常实现为红黑树。

      ​ 你可能需要存储一些键值对,例如存储学生姓名对应的分数:Tom 0Bob 100Alan 100。但是由于数组下标只能为非负整数,所以无法用姓名作为下标来存储,这个时候最简单的办法就是使用 STL 中的 map 了!

    3. 插入与删除操作

      • 可以直接通过下标访问来进行查询或插入操作。例如 mp["Alan"]=100
      • 通过向 map 中插入一个类型为 pair<Key, T> 的值可以达到插入元素的目的,例如 mp.insert(pair<string,int>("Alan",100));或者mp.insert({key,value})
      • erase(key) 函数会删除键为 key所有 元素。返回值为删除元素的数量。
      • erase(pos): 删除迭代器为 pos 的元素,要求迭代器必须合法。
      • erase(first,last): 删除迭代器在image-20210717155426463范围内的所有元素。
      • clear() 函数会清空整个容器。

      下标访问中的注意事项


      ​ 在利用下标访问 map 中的某个元素时,如果 map不存在相应键的元素,会自动在 map 中插入一个新元素(一般都是用次方法插入),并将其值设置为默认值(对于整数,值为零;对于有默认构造函数的类型,会调用默认构造函数进行初始化)。

      ​ 当下标访问操作过于频繁时,容器中会出现大量无意义元素,影响 map 的效率。因此一般情况下推荐使用 find() 函数来寻找特定键的元素。

    4. 查询操作

      • count(x): 返回容器内键为 x 的元素数量。复杂度为 (关于容器大小对数复杂度,加上匹配个数)。
      • find(x): 若容器内存在键为 x 的元素,会返回该元素的迭代器;否则返回 end()
      • lower_bound(x): 返回指向首个不小于给定键的元素的迭代器。
      • upper_bound(x): 返回指向首个大于给定键的元素的迭代器。若容器内所有元素均小于或等于给定键,返回 end()
      • empty(): 返回容器是否为空。
      • size(): 返回容器内元素个数。

    例2

    Dyson Box


    A Dyson Sphere is a hypothetical megastructure that completely encompasses a star and captures a large percentage of its power output. The concept is a thought experiment that attempts to explain how a spacefaring civilization would meet its energy requirements once those requirements exceed what can be generated from the home planet’s resources alone. Only a tiny fraction of a star’s energy emissions reach the surface of any orbiting planet. Building structures encircling a star would enable a civilization to harvest far more energy.

    One day, Moca has another idea for a thought experiment. Assume there is a special box called Dyson Box. The gravitational field in this box is unstable. The direction of the gravity inside the box can not be determined until it is opened.

    The inside of the box can be formed as a 2-dimensional grid, while the bottom left corner’s coordinate is $ (0, 0)$ and the upper right corner’s coordinate is ((2 · 10^5,2 · 10^5) ). There will be n events. In the i-th event, a new cube will appear, whose upper right corner’s coordinate is ((x_i , y_i)) and bottom left corner’s coordinate is ((x_i − 1, y_i − 1)).

    There are two directions of gravity in the box, vertical and horizontal. Suppose Moca opens the box after the i-th event. In that case, she has $ frac{1} {2}$ probability of seeing the direction of the gravity inside the box is vertical, and the other $ frac{1} {2}$ probability is horizontal. And then, she will measure the total length of the outline of all the cubes. If the direction of gravity is horizontal, all the cubes inside will move horizontally to the left under its influence. Similarly, vertical gravity will cause all the cubes to move downward.

    Moca hates probability, so that she is asking for your help. If you have known the coordinates of all the cubes in chronological order, can you calculate the total length of these two cases after each event?

    Input

    The first line contains one integer n (1 ≤ n ≤ 2 · 105 ) – the number of cubes. Each of the following n lines describes a cube with two integers xi , yi (1 ≤ xi , yi ≤ 2 · 105 ). It is guaranteed that no two cubes have the same coordinates.

    Output

    For each of the n cubes, print one line containing two integers – two answers when the the direction of gravity is vertical and horizontal.

    image-20210719103526631

    Note

    In the only example, the inside of the box is as below, and the bold lines mark the outline of all the cubes. After the 1-st event:

    image-20210719103721561

    1.3____Struct 重写 & 在 STL容器中的应用


    #include <iostream>
    #include <iomanip>
    #include <cstdio>
    #include <set>
    #include <algorithm>
    using namespace std;
    
    struct Point
    {
        double x,y;
    
        Point(double _x,double _y){
            x = _x,y = _y;
        }
        bool operator < (const Point& b) const{
            if( x == b.x ){
                return y< b.y;
            }
            return x < b.x;
        }
    
        Point(){}
    };
    
    int main()
    {
        set<Point> se;
    
    
        se.insert({3,24325});
        se.insert({283,25});
        se.insert({21453,2435});
        se.insert({213,35});
        
        for(auto it :se){
            cout << it.x << ' ' <<it .y <<endl;
        }
    
        return 0;
    }
    
    

    2____尺取 & Two Point

    2.1____尺取

    尺取法:顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对指针,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。

    ​ 尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的时候,所以说尺取法是一种高效的枚举区间的方法,是一种技巧,一般用于求取有一定限制的区间个数或最短的区间等等。当然任何技巧都存在其不足的地方,有些情况下尺取法不可行,无法得出正确答案,所以要先判断是否可以使用尺取法再进行计算。

    例3

    Subsequence


    ​ 给定长度为 (n) 的数列整数 (a_0,a_1,···,a_{n-1}) 以及整数 (S) 。求出总和不小于 (S) 的连续子序列的长度的最小值。如果无解则输出 (0)

    Input

    The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

    Output

    For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

    Sample Input

    2
    10 15
    5 1 3 5 10 7 4 9 2 8
    5 11
    1 2 3 4 5
    

    Sample Output

    2
    3
    
    #include <iostream>
    #include <cstdio>
    #define ll long long
    using namespace std;
    const ll INF = 0x3f3f3f3f;
    const int N = 100010;
    
    ll a[N];
    int ans = INF;
    ll sum, s;
    
    int main()
    {
        int n, t;
        cin >> t;
        while (t--){
            cin >> n >> s;
            for (int i = 0; i < n; i++){
                cin >> a[i];
            }
    
            int l = 0, r = 0;
            ans = INF; sum = 0;
            while (1){
                while (r<n && sum<s) sum += a[r++];
                if (sum < s) break;
                ans = min(ans, r-l);
                sum -= a[l++];
            }
            if (ans == INF) ans = 0;
            cout << ans << endl;
        }
        return 0;
    }
    

    例4

    Jessica's Reading Problem


    ​ 一本书有 (P) 页,每页都有a[i]个知识点,知识点可能重复,求最少的连续页数来覆盖所有知识点。

    Input

    The first line of input is an integer P (1 ≤ P ≤ 1000000), which is the number of pages of Jessica's text-book. The second line contains P non-negative integers describing what idea each page is about. The first integer is what the first page is about, the second integer is what the second page is about, and so on. You may assume all integers that appear can fit well in the signed 32-bit integer type.

    Output

    Output one line: the number of pages of the shortest contiguous part of the book which contains all ideals covered in the book.

    Sample Input

    5
    1 8 8 8 1
    

    Sample Output

    2
    
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <set>
    #include <map>
    #define MAX 1000010
    #define LL long long
    #define INF 0x3f3f3f3f
     
    using namespace std;
    int a[MAX];
    map <int, int> cnt;
    set <int> t;
    int p, ans = INF, st, en, sum;
     
    int main()
    {
        scanf("%d", &p);
        for (int i = 0; i < p; i++) scanf("%d", a+i), t.insert(a[i]);
        int num = t.size();
        while (1){
            while (en<p && sum<num)
                if (cnt[a[en++]]++ == 0) sum++;
            if (sum < num) break;
            ans = min(ans, en-st);
            if (--cnt[a[st++]] == 0) sum--;
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    2.2____双指针

    ​ 所谓双指针算法,就是指的是在遍历的过程中,不是普通的使用单个指针进行循环访问,而是使用两个相同方向或者相反方向的指针进行扫描,从而达到相应的目的。

    例4

    数组元素的目标和


    给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。

    数组下标从 0 开始。

    请你求出满足 $A[i]+B[j]=x $的数对 (i,j)。

    数据保证有唯一解。

    输入格式

    第一行包含三个整数 n,m,x分别表示 A 的长度,B 的长度以及目标值 x。

    第二行包含 n 个整数,表示数组 A。

    第三行包含 m 个整数,表示数组 B。

    输出格式

    共一行,包含两个整数 i 和 j。

    数据范围

    数组长度不超过 (10^5)
    同一数组内元素各不相同。
    1≤数组元素≤(10^9)

    输入样例:

    4 5 6
    1 2 4 7
    3 4 6 8 9
    

    输出样例:

    1 1
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 100010;
    int a[N],b[N];
    int n ,m,x;
    
    int main()
    {
        cin >> n >> m >>x;
        
        for(int i =0 ; i < n ; i++){
            cin >> a[i];
        }
        for(int i =0 ; i <m ; i++){
            cin >> b[i];
        }
        
        sort(a,a+n);
        sort(b,b+m);
        
        int j = m - 1;
        int ans = 0;
        for(int i = 0; i < n ; i++){
            while( a[i] + b[j] > x && j >= 0){
                j--;
            }
            if( a[i] + b[j] == x ){
               cout << i << " " << j << endl;
            }
        }
    
        return 0;
    }
    

    3____离散化

    ​ 离散化(Discretization),把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。例如:

    • 原数据:1, 999, 100000, 15;

      处理后:1,3,4,2。

    • 原数据:{100, 200},{20, 50000},{1, 400}

      处理后:{3,4},{2,6},{1,5}。

    ​ 有的时候,我们会发现对于一个序列,它的值域很大,对应算法的复杂度是 Θ(值域) 的。离散化是程序设计中一个常用的技巧,它可以有效的降低时间复杂度。其基本思想就是在众多可能的情况中,只考虑需要用的值。离散化可以改进一个低效的算法,甚至实现根本不可能实现的算法。例如,在建造线段树空间不够的情况下,可以考虑离散化。

    image-20210718112259982

    4____习题

    尺取

    poj2566

    poj2739 1

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 100010;
    int  prime[N],cnt = 0 ;
    bool st[N];
    
    ///  线筛打质数表
    void get_prime(int x)
    {
        for(int i = 2; i <= x; i++){
            if(!st[i])
                prime[cnt++] = i;
            for(int j = 0; prime[j] <= x / i; j++){
    
                st[prime[j]*i] = true;
                if(i % prime[j] == 0)
                    break;
            }
        }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cout.tie(0);
        cin.tie(0);
    
        get_prime(N);
    
        int n;
        while(cin >> n)
        {
            if(n == 0)  break;
            int l = 0, r = 0,ans = 0,sum = 0;
            while(true){
                while( r < cnt && sum < n  ) sum += prime[r++];
    
                if( sum < n) break;
                if( sum == n ) ans ++;
                sum -= prime[l++];
            }
    
            cout << ans << endl;
        }
    
        return 0;
    }
    
    

    poj2100 3

    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    
    typedef long long ll;
    vector<ll> a[1000000];
    
    int main(){
    
        ll n;
        cin >> n;
        ll len = (ll)sqrt(n);
    
        ll l = 1 ,r = 1,sum = 0,ans = 0;
        while(true){
    
            while( r <= len && sum < n ){
                sum += r*r;
                r++;
            }
            if( sum < n ) break;
    
            if( sum == n  ){
    
                a[ans].push_back(r-l);
                for(ll i = l ; i < r; i++){
                    a[ans].push_back(i);
                }
                ans++;
            }
    
            sum -= l*l;
            l++;
        }
    
        cout << ans <<endl;
        for(ll i = 0 ; i < ans; i++){
            ll len = a[i].size();
            for(ll j = 0 ; j < len ; j++){
                cout << a[i][j] << ' ';
            }
            cout << endl;
        }
    
    
        return 0;
    }
    
    

    set

    CodeForces 1287B 4

    #include <bits/stdc++.h>
    using namespace std;
    
    int n,m,ans;
    
    string s[2000];
    set<string> se;
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin >>n >> m;
        for(int i = 0; i < n ; i++){
            cin >> s[i];
            se.insert(s[i]);
        }
    
        for(int i = 0 ; i < n ; i++){
            for(int j = i + 1 ; j < n ; j++){
                string te = "";
                for(int k = 0 ; k < m; k++){
                    if( s[i][k] == s[j][k] ){
                        te += s[i][k];
                    }
                    else{
                        if( (int)(s[i][k] + s[j][k]) == 167 ){
                            te+='E';
                        }
                        else if( (int)(s[i][k] + s[j][k]) == 152 ){
                            te+='T';
                        }
                        else te += 'S';
                    }
                }
    
                if( se.find(te) != se.end()){
                    ans++;
                    //cout <<s[i] << endl <<s[j] << endl << te << endl << endl;
                }
    
            }
        }
        cout << ans/3 <<endl;
        return 0;
    }
    

    map

    Gym 103118D 2

    #include <bits/stdc++.h>
    using namespace std;
    
    int main(){
    
        int n,k;
        cin >> n;
    
        int res1 = 0, res2 = 0;
        map<int, int> ma1, ma2;
        for(int i = 0; i < n; i ++ ){
            int a, b;
            cin >> a >> b;
    
            if(ma1[a]){
                res1 += 2;
            }
            else    res1 += 4;
    
            if(ma2[b]){
                res2 += 2;
            }
            else    res2 += 4;
    
            ma1[a] ++, ma2[b] ++;
    
            if(ma1[a] <= ma1[a - 1])    res1 -= 2;
            if(ma1[a] <= ma1[a + 1])    res1 -= 2;
    
            if(ma2[b] <= ma2[b - 1])    res2 -= 2;
            if(ma2[b] <= ma2[b + 1])    res2 -= 2;
    
            cout << res1 << " " << res2 << endl;
    
        }
    
        return 0;
    }
    
    
  • 相关阅读:
    strace命令跟踪ping调用函数整理
    小知识记录:第XII篇
    学习笔记:《Kali Linux 2 网络渗透测试 实践指南 第2版》之主动扫描(Nmap)
    学习笔记:《Kali Linux 2 网络渗透测试 实践指南 第2版》之被动扫描(Maltego)
    学习笔记:《Kali Linux 2 网络渗透测试 实践指南 第2版》之kali Linux使用基础
    《柯尔特思维教程》-第3章(交互)- 前言
    《柯尔特思维教程》-第2章(组织)- 前言
    《柯尔特思维教程》-第1章(广度)- 前言
    《柯尔特思维教程》-第3章(交互)- 第10节:结果
    《柯尔特思维教程》-第3章(交互)- 第9节:失误-2:错误和偏见
  • 原文地址:https://www.cnblogs.com/hoppz/p/15058719.html
Copyright © 2011-2022 走看看