aaaaa

是个好东西

cpp 复制代码
ios::sync_with_stdio(false);
cin.tie(0);

超级头文件 bits万能头无法在部分编译器使用

cpp 复制代码
// C++ 基础与 I/O
#include <iostream>
#include <cstdio>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstring>

// 容器与数据结构
#include <vector>
#include <list>
#include <deque>
#include <stack>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <bitset>
#include <array>

// 算法与数学函数
#include <algorithm>
#include <cmath>
#include <numeric>
#include <functional>
#include <utility>
#include <iterator>

// 字符串处理
#include <string>
#include <cctype>

// 时间与随机数
#include <ctime>
#include <chrono>
#include <random>

// 智能指针与内存管理
#include <memory>

初始化

cpp 复制代码
long long num = 0, money = 0;

初始化参数列表

cpp 复制代码
Node(int x1, int y1, int x2, int y2, int l)
: x1(x1), y1(y1), x2(x2), y2(y2), lay(l) {}

我chovy 你是不是用迭代器不打括号??!给我打好了啊!

cpp 复制代码
if (px >= (*it).x1 && px <= v.x2 && py <= v.y2)

你是不是先擦除再使用了?!一定要先拷贝一份再用

cpp 复制代码
vec.insert(vec.begin(), (*it));//放到最前端
vec.erase(it);

你是傻逼吗?在用it的迭代中删了it???

不要在迭代器的for中进行删除,要用下标进行访问,记录下标,break出循环后再erase,这样可以避免逆序迭代无法删除的问题。

所有的push_backTMD是从后扔进去!

你有没有手贱把j循环的自增打成i?!

你TM调试信息是不是忘记注释了?!

你有没有把一些变量写在循环里了?!

你有没有多次输入的时候清空变量?!

你TM在贪心/DP中写break了吗?不然怎么跳出去?

输出小数的除法你加(double)A/B了吗?

for输出后你换行了吗?!

取余不变性

前置空格法

cpp 复制代码
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++){
            if(j > 0) cout << " ";      // 防坑:非首元素前加空格
            cout << A[i][j];
        }
        cout << endl;
    }

struct要加分号!!!

注意二维数组坐标问题

注意二维数组题目的初始坐标问题!!!这里是正整数uv,因此在遍历图的时候是从1开始的,那么前置空格法也是要求if(j>1) cout<<" ";

https://www.luogu.com.cn/problem/T419391

cpp 复制代码
#include<iostream>
using namespace std;
int A[1005][1005],n,m;

int main(){
    cin>>n>>m;
    int n1,n2;
    for(int i=0;i<m;i++){
        cin>>n1>>n2;
        A[n1][n2]=1;
        A[n2][n1]=1;
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            
            if(j!=1) cout<<" ";
            cout<<A[i][j];
            
        }
        cout<<endl;
    }
    for(int i=1;i<=n;i++){
        int d=0;
        for(int j=1;j<=n;j++){
            if(A[i][j]) d++;
        }
        cout<<d;
        for(int j=1;j<=n;j++){
            if(A[i][j]) cout<<" "<<j;
        }
        cout<<endl;
    }
}

特殊输入方式

这道题是一次输入一行,但是要转为二维数组。

可以用char temp定义临时字符,然后每次输入都只接受一个字节的数据,然后就可以用temp-'0'转换了。

cpp 复制代码
    cin>>n>>m;
    char temp;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>temp;
            maze[i][j]=temp-'0';
        }
    }
cpp 复制代码
char temp;
for(int i=1;i<=N;i++){
        for(int j=1;j<=M;j++){
            cin>>temp;
            if(temp=='#'){
                maze[i][j]=0;
            }else{
                maze[i][j]=1;
            }
        }
    }

带有空格的整行输入要用getline(cin,s)否则会截断

如果题目给定的后缀表达式非常长,或者中间带有空格换行,cin >> s 遇到空格就会停止读取,导致后面的数字和运算符全部丢失。必须使用 getline(cin, s)

混合数组和字符的输入处理

cpp 复制代码
    string s;
    getline(cin, s);
    stack<long long> st;
    string num = "";
    for (char c : s) {
        //第一个可能是数字也可能是负号,要完整接受一个多位数字:先通过一次性收集齐"."前的字符,然后一次stod变成数字
        if (isdigit(c)) {
            num += c;
        }

特殊的输出方式

要求带精度小数的输出

cpp 复制代码
cout<<fixed<<setprecision(2)<<curv<<endl;

注意 拓扑问题一个节点可以有多个输出边,因此要一直输入

cpp 复制代码
    for(int i=1;i<=N;i++){
        //注意 拓扑问题一个节点可以有多个输出边,因此要一直输入
        while(cin>>v && v){
            cin>>v;
            addEdge(i,v);
            inE[v]++;
        }
    }

排序

桶排序 T407375

https://www.luogu.com.cn/problem/T407375

用vector开桶,计算桶的大小,桶大小=max-min+1/N,

然后输入到各个桶,idx=(ai-min)/N; if(idx==N) idx=N-1; pushback(ai)

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int N;

int a[100005];
//开N个桶,涉及范围max min,单个桶的大小就是range=max-min+1 / N
int main(){
    vector<int> buk[100005];
    cin>>N;
    int min_n=(int)1e9, max_n=0;
    for(int i=0;i<N;i++){
        cin>>a[i];
        max_n=max(max_n,a[i]);
        min_n=min(min_n,a[i]);
    }
    long long range = (long long)max_n - min_n + 1;
    double bk_size = (double)range / N; 
    for(int i=0;i<N;i++){
        
        int idx=(a[i]-min_n)/bk_size;//落入区间是当前值-最小值/桶大小
        if(idx >= N) idx = N - 1; 
        buk[idx].push_back(a[i]);
    }
    for(int i=0;i<N;i++){
        //对各个桶排
        sort(buk[i].begin(),buk[i].end());
        for(int x:buk[i]){
            cout<<x<<" ";
        }
    }
    
}

链表

创建链表

可以用Node *head在堆上创建,这种需要dlete删除。

也可以用Node head;

head.val=10;

head->next=&node1;(指针必须用->访问)

遍历链表

初始节点指向head->next(第一个有值),然后通过cur->val遍历访问。

插入节点(p后面插一个)

new新节点,填值,当前下一个=原先下一个,原先下一个=当前

删除节点(p的下一个)

暂存当前节点,指向下一个的下一个,删除d

查找节点

模板

cpp 复制代码
#include<iostream>
using namespace std;
struct Node{
    int val;
    Node* next;
};

void insert(Node* p,int val){
    Node*q = new Node();
    q->val = val;
    q->next = p->next;
    p->next = q;
}

Node* findf(Node* head, int target){
    Node* cur = head->next;
    while(cur!=NULL && cur->val!=target){
        cur=cur->next;
    }
    return cur;
}
void printn(Node* head){
    Node* cur = head->next;
    while(cur!=NULL){
        cout<<cur->val<<" ";
        cur=cur->next;
    }
}
void deletep(Node* head,int tar){
    Node* cur = head;
    while(cur!=NULL && cur->next!=NULL){
        if(cur->next->val==tar){
            Node* d = cur->next;
            cur->next=cur->next->next;
            delete d;
        }else{
            cur=cur->next;
        }
    }
    
}

int main(){
    Node* head=new Node();
    Node* n1 = new Node();
    n1->val=5;
    head->next=n1;
    Node* n2 = new Node();
    n2->val=10;
    n1->next=n2;
    Node* n3 = new Node();
    n2->next=n3;
    n3->val=15;

    insert(head,10);
    insert(head,23);
    printn(head);
    cout<<endl;
    Node* tar = findf(head,5);
    insert(tar,11);
    deletep(head,10);

    printn(head);
    cout<<endl;
    return 0;
}

静态链表(数组模拟)

创建链表

遍历链表

插入链表

删除链表

查找链表

模板

cpp 复制代码
/*每个结点存一个int型数据
创建一个头结点不存数据的空链表
创建三个结点,数值分别为5、10、15
5->10->15
在链表头部插入数值为10的结点
在链表头部插入数值为23的结点
依次输出当前链表的值,用空格隔开,输出所有值后换行
找到链表值为5的结点,在其后插入一个值为11的结点
删除所有值为10的结点
依次输出当前链表的值,用空格隔开,输出所有值后换行*/
#include<iostream>
using namespace std;
struct Node {
    int val;
    int next;
};
Node nodes[1000];
int cnt = 1;
void insert(int pre, int val) {//在pre后节点加一个
    nodes[cnt].val = val;
    nodes[cnt].next = nodes[pre].next;
    nodes[pre].next = cnt;
    cnt++;
}
void printn(int head) {
    int cur = nodes[head].next;
    while (cur) {
        cout << nodes[cur].val << " ";
        cur = nodes[cur].next;
    }
    cout << endl;
}
int findn(int head, int tar) {
    int cur = head;
    while (nodes[cur].next != 0 && nodes[nodes[cur].next].val != tar) {
        cur = nodes[cur].next;//一定不是自然递增,索引不连续
    }
    return nodes[cur].next;//返回找到的本届点
}

void deleteAll(int head, int tar) {
    int cur = head;
    while (nodes[cur].next != 0) {//其实不用判定当前是否为零,因为从head开始的地址就不为0
        if (nodes[nodes[cur].next].val == tar) {
            nodes[cur].next = nodes[nodes[cur].next].next;
        }
        else {
            cur = nodes[cur].next;
        }
    }
}

int main() {
    int head = 0;
    insert(head, 15);
    insert(head, 10);
    insert(head, 5);


    insert(head, 10);
    insert(head, 23);
    printn(head);
    int f = findn(head, 5);
    insert(f, 11);
    deleteAll(head, 10);
    printn(head);
    return 0;
}

约瑟夫问题 P1996

https://www.luogu.com.cn/problem/P1996

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

//维护一个下标 一个当前计数值 下标用于删除和输出 当前计数值用于判断m 一个剩余数量
int cur=1,cnt=0,rem;

int pp[1005];
int main(){
    int n,m;
    cin>>n>>m;
    //cur是下标 cnt是计数值 每次cur==m都会触发清零 cnt==0;
    //当剩余数量
    rem=n;
    while(rem){
        if(pp[cur]==0){//如果当前人活着
            cnt++;//计数++
            if(cnt==m){//如果这个人正好是cnt第m个就杀了
                cout<<cur<<" ";
                cnt=0;//清零计数
                pp[cur]=1;//标记死人
                rem--;//剩余人数--
            }
        }
        cur++;
        if(cur>n) cur=1;
    }
}

队列

入队

头往后

出队

尾巴往前移

环形队列

STL队列 queue

STL双端队列 deque

STL栈

前中后缀表达式

中缀转前缀

中缀转后缀

前后缀表达式的计算

前缀表达式的计算

前缀从右往左压入栈,遇到运算符弹栈计算。

后缀表达式的计算

后缀从左往右压入栈,遇到运算发弹栈计算。注意顺序是次栈顶<运算>栈顶。

练习 P1449

https://www.luogu.com.cn/problem/P1449

cpp 复制代码
#include <iostream>
#include <stack>
#include <string>
using namespace std;
//
int main() {
    string s;
    getline(cin, s);
    stack<long long> st;
    string num = "";
    for (char c : s) {
        //第一个可能是数字也可能是负号,要完整接受一个多位数字:先通过一次性收集齐"."前的字符,然后一次stod变成数字
        if (isdigit(c)) {
            num += c;
        }
        else if (c == '.') {//如果遇到是.就std
            st.push(stoll(num));
            num.clear();//输入完要清空
        }
        else if (c == '@') {
            break;
        }
        else {
            long long op2 = st.top(); st.pop();
            long long op1 = st.top(); st.pop();

            if (c == '+') {
                //cout << op1 <<"+"<< op2 <<"="<<op1+op2 << endl;
                st.push(op1 + op2);

            }
            else if (c == '-') {
                //cout << op1 << "-" << op2 << "=" << op1 - op2 << endl;
                st.push(op1 - op2);

            }
            else if (c == '*') {
                //cout << op1 << "*" << op2 << "=" << op1 * op2 << endl;
                st.push(op1 * op2);

            }
            else {
                //cout << op1 << "/" << op2 << "=" << op1 / op2 << endl;
                st.push(op1 / op2);

            }
        }


    }
    cout << st.top() << endl;
}

STL

排序sort()

正序

cpp 复制代码
sort(num.begin(), num.end()); 

逆序

cpp 复制代码
// rbegin() 指向最后一个元素,rend() 指向第一个元素的前一个位置
sort(num.rbegin(), num.rend()); 

自定义类型使用sort()

如果 cmp(a, b) 返回 truesort 就认为 a 应该排在 b 的前面。

如果 cmp(a, b) 返回 falsesort 就认为 a 应该排在 b 的后面(或者顺序不变)。

第二关键词排序

cpp 复制代码
// 比较函数:优先按时间升序;时间相同时,按原始编号(idx)升序
bool cmp(const Node& a, const Node& b) {
    if (a.t != b.t) return a.t < b.t;
    return a.idx < b.idx;
}

精度限制输出

cpp 复制代码
cout << fixed << setprecision(10) << ans << endl;

清空/初始化 Memset

对象、多少、大小sizeof

cpp 复制代码
memset(arr, 0, sizeof(arr)); // 将整个数组的每个字节设为0

容器

顺序容器:通过位置来访存元素

容器适配器:集成了基本容器的容器

关联容器:字典,用于表达键值对关系

无序容器:哈希表

Vector

vector初始化长度后,填充不可以用push_back(),否则只是往后加,必须用veci填充。

如果没有初始化长度才可以用push_back().

vector开在main外面。

二维vector

矩形二维vector

可变长一维数组的拼接 与 创建 遍历

这里例子每一行是i的大小递增。

如果要求任意长度,可以用resize(),再push_back进vec4中。

对Vector的遍历

一种是

Resize 操作 例题

带初始值的resize,将行数扩到4,第四行是全0的大小为4的vector

cpp 复制代码
std::vector<std::vector<int>> matrix(3, std::vector<int>(3, 0)); // 初始为 3x3

// 将行数扩容到 4,新增的第4行是一个包含 4 个元素且全为 0 的 vector
matrix.resize(4, std::vector<int>(4, 0)); 

不带初始值的resize,将行数扩到4,第四行是null,在使用的时候必须对列resize。

cpp 复制代码
std::vector<std::vector<int>> matrix(3, std::vector<int>(3, 0)); // 初始为 3x3

// ❌ 危险操作:行数变成了 4,但第 4 行(matrix[3])里面没有分配任何空间!
matrix.resize(4); 

P3613

https://www.luogu.com.cn/problem/P3613

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;
vector<vector<int>> store;

int main(){
    int n,q;
    cin>>n>>q;
    store.resize(n+1);
    int p,i,j,k;
    for(int v=0;v<q;v++){
        cin>>p;
        if(p==1){
            cin>>i>>j>>k;
            if(store[i].size()<=j){store[i].resize(j+1);}
            store[i][j]=k;
        }else{
            cin>>i>>j;
            cout<<store[i][j]<<endl;
        }
    }
    return 0;
}

List

List的遍历

实际上直接for(auto x:lst)即可。

List的查找

找第一个/找所有的val都可以

可以直接用algorithm的函数

find(begin,end,val)

List的删除

删除单个节点

erase(find(10))

删除所有节点

remove(10)

List的插入

在后面插入 push_back

在前面插入 push_front

在i之前插入

L.insert(i,100)

在i之后插入

auto i=find(5)

i++

L.insert(i,1000)

翻转链表

List与Vector的转换

1. Vector转List

cpp 复制代码
std::vector<int> myVec = {1, 2, 3, 4};
// 利用迭代器范围直接构造 list
std::list<int> myList(myVec.begin(), myVec.end()); 

2. List转Vector

这是最直接的方法。std::vector 提供了一个接受迭代器范围的构造函数,可以直接接收 std::list 的头尾迭代器进行初始化:

cpp 复制代码
std::list<int> myList = {5, 6, 7, 8};
// 利用迭代器范围直接构造 vector
std::vector<int> myVec(myList.begin(), myList.end()); 

应用

快排回去

List排序很慢

cpp 复制代码
std::list<int> myList = {...}; // 假设包含大量无序数据

// 1. 将 list 转换为 vector
std::vector<int> tempVec(myList.begin(), myList.end()); 

// 2. 对 vector 进行高效排序
std::sort(tempVec.begin(), tempVec.end()); 

// 3. 将排好序的数据拷贝回 list
10myList.assign(tempVec.begin(), tempVec.end()); 

Map 字典(增删改查 且有不重复唯一键 带值去重)

不支持随机访问,必须用it迭代器

会对key建立索引。

如果是自定义类型的key,必须实现<

基本用法

find返回的是迭代器,如果是尾后迭代器就是没找到。

遍历的时候需要用i->first ->second输出元素。

练习

T424396

https://www.luogu.com.cn/problem/T424596

P1918

https://www.luogu.com.cn/problem/P1918

Set(增删改查 且本身就是可作为索引 唯一 去重)

不支持随机访问 必须用it迭代。

注意,set是有序的,是可以建立索引和遍历的。集合是无序的。

注意,set的插入是insert。

set的遍历

输出是有序的 这里插入10 15 11但是输出是唯一且有序的 7 8 10 11 15.

练习

带重复的第k小整数,但是要求不重复的顺序。最核心的痛点就是去重。

注意,set不支持随机访问,必须顺序遍历

cpp 复制代码
//迭代器不可以跳变,只能一个个叠加上去。
    auto it=s.begin();
    for(int i=0;i<k-1;i++){
        it++;
    }
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

set<int> s;
int main(){
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++){
        int temp;
        cin>>temp;
        s.insert(temp);
    }
    if(k > s.size()){
        cout << "NO RESULT" << endl;
        return 0;
    }
    //迭代器不可以跳变,只能一个个叠加上去。
    auto it=s.begin();
    for(int i=0;i<k-1;i++){
        it++;
    }
    cout<<*it<<endl;
}

set和Map的upper_bound和lower_bound

当查找基于红黑树实现的容器,比如set和map,都可以用bound函数找到第一个满足大于这个值/小于这个值的值。

P5250

https://www.luogu.com.cn/problem/P5250

正确解法:利用 lower_bound

std::set 内部是红黑树,支持 O(log⁡N)的查找。我们可以利用 s.lower_bound(val)

  • 它会返回第一个 大于等于 val 的元素的迭代器。

策略:

  1. 精确查找 :先 find(val),如果有直接取走。
  2. 模糊查找
    • lower_bound(val) 找到第一个≥val 的数,记为 it_high(候选1:偏大的)。
    • 如果 it_high 不是 begin(),那么 it_high 的前一个数 prev(it_high) 一定是 <val 的最大数,记为 it_low(候选2:偏小的)。
    • 比较这两个候选者与 val 的差值。题目要求"一样接近取较短",意味着如果差值相等,优先取 it_low

找第一个大于等于val的迭代器

cpp 复制代码
auto it_high=s.lower_bound(val)

然后就可以用

cpp 复制代码
*(--it_high)

表达后一个

cpp 复制代码
*(++it_high)
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

set<int> s;
//vector<int> vec;
int main(){
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int op=0;
        cin>>op;
        if(op==1){
            int val=0;
            cin>>val;
            auto it=s.find(val);
            if(it==s.end()){//如果没招到
                s.insert(val);
            }else{
                cout<<"Already Exist"<<endl;
            }
        }else if(op==2){
            int val=0;
            cin>>val;
            auto it=s.find(val);
            if(s.empty()){
                cout<<"Empty"<<endl;
            }else if(it!=s.end()){
                s.erase(val);
                cout<<val<<endl;
            }else{//如果找不到长度一样的,就找最小的 直接上二分
                auto it_high=s.lower_bound(val);//去找第一个满足大于val的迭代器
                int chose=0;
                if(it_high==s.end()){//如果所有的都不满足,说明都太短了
                    chose=*(--it_high);
                }else if(it_high==s.begin()){//所有的都大于,都太长了
                    chose=*it_high;
                }else{
                    int v_high=*it_high;
                    int v_low=*(--it_high);

                    if(v_high-val<val-v_low){//更近就选更近的,一样就选短的
                        chose=v_high;
                    }else{
                        chose=v_low;
                    }

                    
                }
                s.erase(chose);
                cout<<chose<<endl;
            }
            
            
        }
    }
}

哈希(超大范围的不重复统计)不咋考

一般都需要offset偏移量

需要将一个大范围压缩到一个小范围时,比如key1的值是-1亿,key4是一亿,就需要映射到小桶中。因为数组不能开1e7的桶。

特性 std::map unordered_map (Hash)
底层结构 红黑树 (平衡二叉树) 哈希表 (数组+链表)
查找/插入速度 logN 1
数据顺序 自动排序 (从小到大) 无序 (乱序)
内存占用 较小 (每个节点只需存左右孩子指针) 很大 (需要预留大量空桶以防冲突)
支持有序访问

Unordered_Map和Unordered_Set

二分查找(寻找一个数/最大值最小值 必须有序)

相关推荐
OpenApi.cc1 小时前
神经网络结构驱动+数据结构分析
数据结构·人工智能·神经网络
菜菜的顾清寒2 小时前
力扣HOT100(42)链表-随机链表的复制
算法·leetcode·链表
lqqjuly2 小时前
模型剪枝与稀疏化:理论、算法与可运行实现
人工智能·算法·剪枝
喵星人工作室2 小时前
C++火影忍者1.1.2
开发语言·c++
逻辑君2 小时前
Foresight研究报告【20260011】
人工智能·线性代数·算法·矩阵
珊瑚里的鱼2 小时前
【动态规划】不同路径Ⅱ
算法·动态规划
basketball6162 小时前
C++ 中的 ptrdiff_t 详解
开发语言·c++
星恒随风2 小时前
C语言数据结构排序算法详解(下):冒泡排序、快速排序、归并排序和计数排序
c语言·数据结构·笔记·学习·排序算法
wunaiqiezixin2 小时前
互斥锁与自旋锁的区别
c++