2021_07_20(双指针&尺取&离散化&stl部分容器介绍&struct重写)

本文解析了四道算法题目,涉及连续质数求和、自然数序列平方和、字符串匹配及图形周长计算等问题,通过滑动窗口、哈希表等技术解决,并介绍了STL容器的实用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - Sum of Consecutive Prime Numbers

题意:给定一个正整数,查询有多少个满足其和可以用连续的质素表示。

思路:因为这道题本身num不会很大(10000),其中包含的质数更少了,完全可以找出所有的素数,然后使用尺取。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
vector< ll > v;
ll sum[10010];
int main()
{
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    v.push_back(2);
    for( ll i = 3; i <= 10000; i++ ){
        int f = 1;
        for( ll j = 2; j <= sqrt( i * 1.0 ); j++ ){
/*

该错误显示sqrt函数后面参数只能是long double 、float或者double类型的数据,
如果给它一个整形的参数,系统报错,不知道该用哪个。有两个方法,一个是使用强转,
一个是使用乘上1.0,让系统自动转化为小数。

*/
            if( i % j == 0 ){
                f = 0;
                break;
            }
        }
        if( f ){
            v.push_back(i);
        }
    }//这道题首先需要素数打表,其实有更高级的做法--素数筛,这里先不说。
    ll len = v.size();
    sum[0] = 0;
    for( ll i = 1; i < len; i++ ){
        sum[i] = sum[i - 1] + v[i - 1];
    }//最开始想用前缀和,但是其实多此一举,后的题会说到这里。
    ll num;
    while( cin >> num && num != 0 ){
        ll l = 0, r = 0;
        ll res = v[0];
        ll cnt = 0;
        while( l < len && r < len ){
            while( res < num && r < len ){
                ++r;
                res = sum[r] -sum[l] + v[r];
            }
            if( res == num ){
                ++cnt;
                ++l;
                res = sum[r] -sum[l] + v[r];
            }
            while( res > num && l < len){
                ++l;
                res = sum[r] -sum[l] + v[r];
            }
        }
        cout << cnt << endl;
    }
    return 0;
}

C - Graveyard Design

 题意:输入一个数(1 <= n <= 10^14),查看有多少种序列满足这种序列中的数是连续的自然数且 序列中各个位置上的数平方的和等于输入的数。

思路:最开始做这道题的时候,先后经历了超时,数组溢出,哭了...,后面发现理解的尺取有问题,难受,可怕就在上面那题居然以这样的方式过了,难受.....

这道题也相当于是尺取的板子。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    ll num;
    cin >> num;
    ll l = 1, r = 1;//刚开始双指针都指向头
    ll res = 0;
    ll len = sqrt( num * 1.0 );//减小范围
    vector< pair< ll, ll > > v;//用一个pair来记录这个序列的左右边界
    while( true ){
    while( res < num && r <= len){
            //如果当前尺度还是小于num,那么右指针一直往右移动,但是要保证不要越界。
            res += r * r;
            ++r;
        }
        //如果从上面的while退出了,说明要么res >= num,或者 r > len;
     
        if( res < num ){
            break;
        }
        //如果经过了上面的while,还是小于num,
        //那就说明这个序列永远都不可能再满足条件了,因为r已经到最尾巴了.

        
        if( res == num && l <= len){
            v.push_back( make_pair( l, r - 1 ) );
        }
        //如果满足上述条件,那么就加入队列中

          res -= l * l;
          ++l;
        //这里为了方便代码书写,我们规定不论是大于还是等于,都把l向右移动。
    }
    len = v.size();
    cout << len << endl;
    for( ll i = 0; i < len; i++ ){
        cout << v[i].second - v[i].first + 1;
        for( ll j = v[i].first; j <= v[i].second; j++ ){
            cout << " " << j;
        }
        cout << endl;
    }
    return 0;
}

D - Hyperset

 题意:输入长度为k的n个字符串,且这个字符串只包含'S','E','T'三种字符,同时n个字符串都是不相同的,找出有多少对(三个为一队)满足对应位置上的字符要么相等,要么互补(SET)。

思路:任意枚举两个字符串,通过这两个字符串,找出对应的字符串,然后在整个字符串中寻找有没有这样的字符串。注意这种思想以后会常常遇到:一般就是常数级的,但是硬跑就会超时,这样就考虑合并。这道题有比较巧妙的地方:1、通过ascall码来记录 2、int 和 char,string的转换。 int sum = 'A' + 'B' + 'C'; string s; s += sum - 'A' - 'B';        PS:结果要除以 3。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
int sum = 'S' +'E' +'T';
int main()
{
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    int n, k;
    cin >> n >> k;
    string s[1510];
    set< string > st;
    for( int i = 0; i < n; i++ ){
        cin >> s[i];
        st.insert( s[i] );
    }
    long long ans = 0;
    for( int i = 0; i < n; i++ ){
        for( int j = i + 1; j < n; j++ ){
            string res;
            for( int f = 0; f < k; f++ ){
                if( s[i][f] == s[j][f] ){
                    res += s[i][f];
                }
                else{
                    res += sum - s[i][f] - s[j][f];
                }
            }
        ans += st.count( res );    
        }
    }
    cout << ans / 3;
    return 0;
}

B - Dyson Box

 题意:给定n个方块的位置,有n种情况,每种情况都是在上面情况上累加的。分别计算在重力向下和向左的情况下,组成的图形的周长。

思路:这是一道思维+stl容器题。很明显每个正方体的周长贡献正常是4,但是在某些情况下,贡献就会减少。拿重力向下的来说,当它所在列本来就有其他方块的时候,它的贡献值就会-2,当它的前一列(后一列)的方块总数大于自己这一列的时候方块总数就会-2。重力方向向左也是一样的。如何记录?使用2个map来分别记录每一行和每一列的方块数量。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
map< ll, ll > mp1, mp2;
int main()
{
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    ll n, x, y;
    cin >> n;
    ll vans = 0, hans = 0;
    while( n-- ){
        cin >> x >> y;
        vans += 4; hans += 4;
        if( mp1[x] )
            vans -= 2;
        if( mp2[y] ) 
            hans -= 2;
        if( x != 0 && mp1[x - 1] > mp1[x] )//注意细节,不要数组越界了
            vans -= 2;
        if( y != 0 && mp2[y - 1] > mp2[y] )
            hans -= 2;    
        if( mp1[x + 1] > mp1[x] )
            vans -= 2;
        if( mp2[y + 1] > mp2[y] )
            hans -= 2;
        ++mp1[x]; ++mp2[y];//注意这个不能放在前面,要先检查了再放
        cout << vans << " " << hans << endl;
    }
    return 0;
}

 总结:

学习了更多的常用stl容器,学习了基本的尺取知识和经典习题,对离散化有了一定的了解。离散化(Discretization),把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小

vector:

v[ i ]:

v.empty( );

v.size();

v.clear( );

初始化:vector初始化总结

set :

s.clear( );

s.insert( );

s.erase( );

s.erase( first,last )  //删除迭代器范围内的所有元素

s.count( ) //查询这个数是否存在,存在返回1,反之返回0

s.empty( );

s.size();

find(x): 在 set 内存在键为 x 的元素时会返回该元素的迭代器,否则返回 end()。

map:

m.count( );

m.find( );

s.empty( );

s.size();

 

vector, 变长数组,倍增的思想
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    支持比较运算,按字典序

pair<int, int>
    first, 第一个元素
    second, 第二个元素
    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)

string,字符串
    size()/length()  返回字符串长度
    empty()
    clear()
    substr(起始下标,(子串长度))  返回子串
    c_str()  返回字符串所在字符数组的起始地址

queue, 队列
    size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素

priority_queue, 优先队列,默认是大根堆
    size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

stack, 栈
    size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素

deque, 双端队列
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []

set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)

    set/multiset
        insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器
    map/multimap
        insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()

unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)
    不支持 lower_bound()/upper_bound(), 迭代器的++,--

bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []

    count()  返回有多少个1

    any()  判断是否至少有一个1
    none()  判断是否全为0

    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~
    flip(k) 把第k位取反

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值