NEUQ寒假训练心得

动态规划

1.组合数是指不强调顺序(即123与321是同一个集合,只算一次),排列数强调顺序(即即123与321是不同的集合,算两次)。

如果求组合数就是外层for循环遍历物品内层for遍历背包

如果求排列数就是外层for遍历背包内层for循环遍历物品

2.01背包是先遍历物品再遍历重量(滚动数组是需要重量倒序遍历),完全背包需要(滚动数组是需要重量正序遍历)),求解方法类,多少种类(需要参考3-4条的情况)。

3.动态规划一般为最大最小类问题(入手点为递归式子)

string知识点

1.       st1.find('a',1);后面的数字代表从什么位置开始查找  (无迭代器)

int post=b.find(a); cout<<post<<endl;

 2         string s2 = s1.erase(0, 2);               string& erase(int pos=0, int n=npos);
s.erase(s.begin()+a-1,s.begin()+b);  (左闭右开)

erase函数使用迭代器来指定要删除的范围而std::string::begin()返回一个指向字符串第一个字符的迭代器。通过将偏移量(如a - 1b)与begin()返回的迭代器相结合,可以得到正确的迭代器位置,从而指定要删除的字符范围

 erase的作用是从位置 pos 开始 , 删除长度为 n 的子字符串 , 如果 n 的值超过了字符串的长度 , 那么整个字符串都会被删除 ;

 3        string a=s1.insert(2,s2);           string &insert(int pos, const string &s);

insert作用是在字符串的指定位置 pos 插入另一个字符串 s , 字符串类型可以是 char* 类型 , 也可以是 string 类型             (用法类似于substr)

 4          string z=s.substr(5,3);         //获得字符串s中从第5位开始的长度为3的字符串

string substr(int pos = 0, int n = npos) const; substr函数使用基于位置的无符号整数作为参数,直接指定起始位置和长度,不需要通过迭代器来操作
substr会截取字符串的一部分 , 并返回一个新字符串 ; 截取的起始位置是 pos , 截取的字符数量 n ; 如果 n 的值超过了字符串的长度 ; 那么整个字符串都会被截取 ; 

5.string的replace()用法   (replace的用法同erase一样)

words.replace(words.begin()+pos,words.begin()+pos+a[i].size(),l);

第一个是对应的初始位置,第二个是结束,最后想要替换的string数据

6.涉及string的数据遍历情况时候一定要str.size()-1

7.stoi函数是C++标准库中的一个函数,用于将字符串转换成整数

  • str:待转换的字符串(支持含前导空格和正负号)
  • idx(可选):输出参数,记录起始字符位置索引
  • base(可选):指定进制(2~36,0表示自动推断)

int stoi(const string& str, size_t* idx = 0, int base = 10);

stoi(str, 0, 2); //将字符串 str 从 0 位置开始到末尾的 2 进制转换为十进制

    queue的知识

    q.push(x);//从队尾加入一个数x(queue的知识)

    q.pop();//弹出队首元素

    q.front();//返回队首元素

    q.size();//返回队列长度

    q.empty();//如果队列为空,返回true,否则返回false

    scanf知识点

    1"%d %d"    显式空格:匹配输入中的任意数量连续空白字符(空格、Tab、换行等)    "12\n34" → 读取12和34
    "%d%d"    隐式处理:%d自动跳过前导空白字符,但两个%d之间不允许有其他非空白字符    "12\t34" → 读取12和34(两种方式都是正确的,但一般使用第二种)

    图论知识点

    1.图的存储方式:邻接矩阵(即二维数组),邻接表(数组加链表)

    2.上下左右遍历方式                       第一种方法

    dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};// 表示四个方向
    for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
                int nextx = curx + dir[i][0];
                int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
    }

     第二种方法

    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    for(int i=0;i<4;i++)
        {
            dfs(x+dx[i],y+dy[i]);
        }

    3.在BFS中,必须在节点入队时立即标记,避免重复访问(visited设置为true

    4.dijkstra算法(模板)(处理单源最短路径

    int grid[550][550];  // 存放所有的边,例如 edges[i][j] 代表从i到j的距离
    int dist[550];  // 记录当前所有点到起点的距离
    int vis[550];  // 标记当前的点是否被踢出
    for(int i = 0; i < n; i++)
        {
            int index = 0;//标定源顶点
            int min;//min 初始化为极大值 INF(即 0x7fffffff),表示“未找到有效节点”
            for (int j = 0; j < n; j++)
            {
                if (!vis[j] && dist[j] < min)//找出未被标记过的源顶点以及距离最近的节点
                {
                    min = dist[j];
                    index = j;
                }
            }
            vis[index] = true;
            for (int h = 0; h < n; h++)
            {
                //限定条件可以再补充一个if (grid[u][v] != INF && vis[v] == false){}
                if (dist[index] + grid[index][h] < dist[index])//及时更新最小距离并存储
                {
                    dist[index] = dist[index] + grid[index][h];
                }
            }
    
        }
    

    5.Floyd 算法(模板)(处理多源最短路径)

    // 开始 floyd
        for (int k = 1; k <= n; k++) {//遍历各个节点来寻找最短距离
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    grid[i][j] = min(grid[i][j], grid[i][k] + grid[k][j]);
                }
            }
        }

    6.并查集(模板)(寻找元素之间的关系)——判断两个元素是否在同一个集合里的时候

    #include <iostream>
    using namespace std;
    const int MAXN = 1e3;
    int pa[MAXN];
    int find(int x)//3.查找对应的祖先,即最后输出结果
    {
    	if (x == pa[x])return x;
    	else {
    		pa[x] = find(pa[x]);//父节点设为根节点,路径压缩
    		return pa[x];
    	}
    }
    void join(int i, int j)//2.添加元素之间的关系
    {
    	int v = find(i);
    	int u = find(j);
    	if (v == u)return;
    	pa[v] = u;//将i的祖先指向j的祖先
    
    }
    void init(int n)//1.初始化节点
    {
    	for (int i = 1; i <= n; i++)
    	{
    		pa[i] = i;
    	}
    }

    6.连通分量是图论中的核心概念,指一个无向图中,任意两个节点之间都存在路径的 最大子图。 

    若图中有3个互不连通的子图,则连通分量数为3。

     计算方法:

    1. 深度优先搜索(DFS)

    适用场景:节点数较少(如n ≤ 1e4),代码简单直观。
    实现步骤

    1. 初始化访问标记数组 visited[](全为0)。
    2. 遍历所有未访问节点,对每个节点启动DFS。
    3. 每次DFS完成:连通分量数+1。
    void dfs(int u, vector<bool>& visited, vector<vector<int>>& graph) {  
        visited[u] = true;  
        for (int v : graph[u]) {  
            if (!visited[v]) dfs(v, visited, graph);  
        }  
    } //遍历整个图的节点
     
    int countComponents(int n, vector<vector<int>>& edges) {  
        vector<vector<int>> graph(n);  
        vector<bool> visited(n, false);  
        int cnt = 0;  
        // 建图  
        for (auto& e : edges) {  
            graph[e[0]].push_back(e[1]);  
            graph[e[1]].push_back(e[0]);  
        }  
        // 计算连通分量  
        for (int i = 0; i < n; i++) {  
            if (!visited[i]) {  
                dfs(i, visited, graph);  
                cnt++;  
            }  
        }  
        return cnt;  
    }  
    简化版本:
    void dfs(int x)
    {
        v[x]=1;
        for(int i=0;i<n;i++)
        {
            if(!v[i]&&g[x][i]==1)
            {
                dfs(i);
            }
        }
    }
    int ltfl()//求取其中的联通分量
    {
        int cnt=0;
        memset(v,0,sizeof(v));//重点
        for(int i=0;i<n;i++)
        {
            if(!v[i])
            {
                dfs(i);
                cnt++;
            }
        }
        return cnt;
    }
    2. 广度优先搜索(BFS)

    适用场景:避免递归栈溢出风险(大规模图)。
    核心逻辑:用队列代替递归栈,逐层遍历邻接节点。
    时间复杂度:与DFS相同(O(V+E))。

    3. 并查集(Union-Find)

    适用场景:动态连通性问题(支持合并操作的高效处理)。
    实现步骤

    1. 初始化父节点数组 parent[](每个节点指向自身)。
    2. 遍历所有边,合并两个端点所属集合。
    3. 统计根节点数量(即连通分量数)。

    vector使用时注意

    1.vector<int> v[maxn] 与 vector<int> v(maxn) 的对比

    声明方式数据结构类型内存分配位置元素类型

    vector<int> v[maxn]静态数组:包含 maxn 个 vector<int> 对象栈内存(自动存储期)每个元素是独立的 

    图的邻接表存储vector<int> v[maxn]每个节点对应独立的邻接链表(矩阵)

    vectorvector<int> v(maxn)单个动态数组:一个 vector 对象,内含 maxn 个 int 元素堆内存(动态分配)所有元素为 int 类型

    预分配固定长度的一维数组vector<int> v(maxn)内存连续,适合数值计算,常用一维数组
    • 前者创建多个容器对象(数组),后者创建单个容器对象并初始化其元素数量
    • 示例:若 maxn=3,则:
      • v[maxn]:3个独立 vectorv[0]v[1]v[2]),每个初始为空
      • v(maxn):1个 vector,包含3个默认初始化的 int 元素(值为0)

    利用vector<int> v[maxn]时候使用v[a].push_back(a1)时遍历注意:

    for(int j=0;j<v[a].size();j++){//看看是不是敌对关系
    			if(v[a][j]==b)
    				flag=0;
    		}

    2.vector的resize使用方式

    作用:动态调整容器中元素数量(size),可扩展或缩减容量。

    void resize(size_type n);                 // 默认填充值初始化元素  
    void resize(size_type n, const T& val);   // 用指定值val填充新增元素  
    vector<int> vec = {1,2,3};  
    vec.resize(5);        // 变为 {1,2,3,0,0}(int默认0)  
    vec.resize(2);        // 变为 {1,2}  
    vec.resize(4,  10);   // 变为 {1,2,10,10}  

    set功能

    set功能:有序性唯一性        vector不具有其功能

    1.C++标准库中的 set 容器具有自动排序功能。其存储的元素会默认按升序排列(从小到大),且排序规则可通过自定义比较函数调整

    2.去重存储:自动过滤重复元素

    • 原理set 是C++标准库中的有序关联容器,通过红黑树实现,自动保证元素的唯一性
    • 代码行为:当执行 s.insert(z) 时,若 z 已存在,set 会拒绝插入新副本。
    • 示例:输入序列 [5, 5, 3, 3, 3]set 中仅存储 {3, 5}

     2.std::greater 是C++标准库提供的比较器,可以直接用于实现降序排序。

    (如有其他元素可以自定义排序)

    #include <iostream>  
    #include <set>  
    #include <functional>  // 包含 greater  
     
    using namespace std;  
     
    int main() {  
        // 使用 greater 实现降序集合  
        set<int, greater<int>> descendingSet;  
     
        descendingSet.insert(10);   
        descendingSet.insert(30);   
        descendingSet.insert(20);   
     
        // 遍历输出(降序)  
        for (int val : descendingSet) {  
            cout << val << " ";  // 输出结果:30 20 10  
        }  
     
        return 0;  
    }  

     3.set的插入只有s.insert(),无其他的插入方法(不像vector除了v.push_back,或者cin>>v[i]一类的插入方式)

       s.size()的作用是得出集合中的元素个数。
       s.empty()是判断该集合是否为空。 
       s.clear()是用来清空集合中的元素。 
       s.count()是查找出现在集合中的元素的个数,但是由于set有唯一性,一般值为1或0,类似于s.find()判断是否在该集合中出现

      s.erase()函数是用来删除指定的元素,只能删除目标值,只能删除一个元素

    遍历元素的方式

    for (auto it = a.begin(); it != a.end(); it++) {
    	cout << *it << " ";//这里it前面的*我们就把它理解成指针
    }
    
    for (auto it : a)
    {
    	cout << it << " ";
    }

    map使用时注意的点

    map<int,double>mp(10001);  // map构造函数不接受容量参数  

    map初始化错误

    后果:编译报错(no matching constructor)。
    修复:删除无效参数:

    小知识点

    1. __gcd(x, y)

    求两个数的最大公约数,如__gcd(6, 8) 就返回2。在 algorithm 库中。

    2.灵活运用i%2这种处理循环问题(滚动数组)(买卖股票的最佳时机)---约瑟夫环问题

    10.vector<string> a如果想要询问存入元素的长度可以使用a[i].size()或a[i].length()

    3.在输入getline(cin,a)之前必须记得要输入getchar()过渡

    在主函数中,读取n之后使用getchar()是为了吸收换行符,因为cin >> n会留下换行符在输入流中,如果不处理,接下来的getline会读取到空行

    4.九宫格PAT天梯赛  L1-104九-团体程序设计天梯赛(看点:九宫格遍历方式)图2为暴力法

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 11;//防止在下面越界访问
    const int n = 9;//九宫格的范围1-9
    int a[N][N];
    int vis[N];//记忆化数组,防止出现重复数字
    int gp[3][2] = {
      
      {1,3},{4,6},{7,9}};//便捷遍历数组1-3,4-6,7-9
    void solve(){
    	int f = 1;			//标记该九宫格是否合法 
    	for(int i = 1;i <= n;i++){
    		memset(vis,0,sizeof(vis));
    		for(int j = 1;j <= n;j++){
    			cin >> a[i][j];
    			if(a[i][j] >= 10 || a[i][j] <= 0){	//判断输入是否合法 
    				f = 0;//切记不能忘记
    				continue;		//防止在下面越界访问 
    			}
    			if(vis[a[i][j]])f = 0;			//判断每一行是否合法 
    			else vis[a[i][j]] = 1;
    		}
    	}
    	if(f){
    		//判断每一列是否合法,巧妙利用i与j的位置关系进行与列的遍历
    		for(int j = 1;j <= n;j++){
    			memset(vis,0,sizeof(vis));
    			for(int i = 1;i <= n;i++){
    				if(vis[a[i][j]])f = 0;
    				else vis[a[i][j]] = 1;
    			}
    		}
    	}
    	if(f){
    		//判断每一宫格是否合法 
    		for(int g = 0;g < n;g++){//表示九宫格的个数1-9
    			memset(vis,0,sizeof(vis));
    			for(int i = gp[g / 3][0];i <= gp[g / 3][1];i++){//行优先从1-3只有g从4开始才会到4-6
    				for(int j = gp[g % 3][0];j <= gp[g % 3][1];j++){//列优先从1-9遍历时刻随着g变化
    					if(vis[a[i][j]]) f = 0;
    					else vis[a[i][j]] = 1;
    				}
    			}
    		}
    	} 
    	if(f)cout << "1" << endl;
    	else cout << "0" << endl;
    }
     
    int main(){
    	int t;cin >> t;
    	while(t--){
    		solve();
    	}
    	return 0;
    }
    for (int i = 0; i < 3; i++)//利用总和统计结果
    {
        for (int j = 0; j < 3; j++)
        {
            int sum = 0;
            for (int x = 0; x < 3; x++)
            {
                for (int y = 0; y < 3; y++)
                {
                    sum += a[i * 3 + x][j * 3 + y];  //这里运用四层循环依次遍历,可以代入数据自己模拟一遍来体会此技巧
                }
            }
            if (sum != 45)  //每三行三行三列为一个小九宫格,判断一次
            {
                flag = 0;
                break;
    
            }
    
        }
    
    }

    5.unordered_map<int,int>a || a[i].count(t2)

    a是一个包含n个unordered_map<Int,int>类型元素的数组,a[i]表示访问数组a中第i个 unordered_map容器。(涉及到哈希函数)    而count统计出现的次数,只会返回1或0.

    map<int,int>a || a.count(t2) 其中count统计出现的次数,不需要a[i]前缀

    unordered_map一定要注意要键值对,同时要考虑是第几对容器问题

    6. ceil()向上取整   eg:ceil(2.8)->3

    7.min=INT_MAX,max=0一般两个数取的值

    const int inf=0x3f3f3f3 十六进制的无穷大

    9.vector<queue<char>> a(n+1)  单一动态数组

    一个vector对象,内含 n+1 个 queue<char> 元素

    vector<queue<char>> a[n+1](x)

    10.遍历stack或queue里面的元素时必须要使用while(!a.empty())

    不使用for遍历容器的原因

    • queue::pop() 会减少队列长度,导致 q1.size() 在循环中持续变化
    • for 循环的终止条件 i < q1.size() 每次比较时,右侧值逐步递减,而左侧 i 递增,二者呈收敛趋势

    11.排序思想:可以利用“-”号灵巧的sort()不需要重载,之后再输出原来的结果

    【GPLT】【2021天梯赛真题题解】【231分】(L2-3清点代码库)

    12.确定还是的问题

    小技巧:行放行上,列放列上,剩下依据情况分析

    vector<vector<int>> grid(n, vector<int>(m, 0));
        for (int i = 0; i < n; i++) {//表示行
            for (int j = 0; j < m; j++) {//表示列
                cin >> grid[i][j];
            }
        }
    
        // 从左侧边,和右侧边 向中间遍历
        for (int i = 0; i < n; i++) {//列的元素对应行遍历
            if (grid[i][0] == 1) dfs(grid, i, 0);//第i行第0列(即矩阵的第一列元素)
            if (grid[i][m - 1] == 1) dfs(grid, i, m - 1);//第i行第m-1列(即矩阵的最后一列元素)
        }
        // 从上边和下边 向中间遍历
        for (int j = 0; j < m; j++) {//行的元素对应列的遍历
            if (grid[0][j] == 1) dfs(grid, 0, j);//第0行第j列元素(即矩阵的第一行元素)
            if (grid[n - 1][j] == 1) dfs(grid, n - 1, j);//第n-1行第j列元素(即矩阵的最后一行元素)
        }

    13.使用string的时候一定要记得灵活运用string的字典序排列。

    使用范围举例:比较日期时间

    14存储数据灵活使用map,尤其是多次重复在一个堆内叠加数据,map的排序只能在中排序,如果是值排序的利用vector+sort方式

    【全网最细PAT题解】【PAT乙】1032 挖掘机技术哪家强(比较经典的map导vector)

    15.连续因子数(L1-006)记住模板

    团体程序设计天梯赛 -- 练习集 (L1合集)

    16.int isalnum(int c);

    • 所属头文件:C语言 <ctype.h> / C++ <cctype>
    • 核心功能
      判断参数 c 是否为字母(A-Z, a-z) 或 数字(0-9)
      • 若满足条件,返回非零值(具体值取决于编译器实现,通常为1)。
      • 否则返回 0

     eg;  while (isalnum(c = get_next_char()) || c == '_') { ... }

     17.to_string 

    用于将数值类型部分内置类型转换为对应的字符串形式

    • 支持类型
      • 整型:intlonglong long 及其 unsigned 版本。
      • 浮点型:floatdoublelong double
    • 不支持类型
      • char(需用 std::string(1, c) 或 std::to_string(static_cast<int>(c)))。
      • 字符串字面量(如 const char*,需直接构造 std::string)。
    #include <string>  
    string str = to_string(42);  // 结果:"42"  

    18.it->a与it.a的区别

    • it->a
      • STL迭代器:如 map<int, string>::iterator it,访问 it->first
      • 智能指针:如 std::unique_ptr<Data> ptr,访问 ptr->a
    • it.a
      • 对象成员访问:如结构体变量、类实例。
      • 引用类型:如 Data& ref = obj; ref.a = 30;

    19.两种迭代器的区别辨析 (传统,范围)

    特性for(auto it=a.begin(); it!=a.end(); it++)for(auto it : a)
    it的类型迭代器(如 vector<int>::iterator元素值或引用(取决于 auto 修饰符)
    访问元素方式需解引用(*it直接访问 it
    修改原容器元素支持(通过 *it = value若用 auto& it 支持,否则仅副本
    适用场景需要索引/复杂迭代逻辑(如删除元素)简单遍历只读或需简化代码时

    20.strcmp字符

    strcmp 是 C/C++ 中用于比较两个字符串的库函数,其核心功能是通过 逐字符 ASCII 码值比较 来确定字符串的大小关系。

    #include <string.h>
    int strcmp(const char *str1, const char *str2);
    • 参数str1 和 str2 是要比较的两个字符串,需以 \0 结尾。
    • 返回值
      • 0:两字符串完全相等。
      • 正整数str1 大于 str2
      • 负整数str1 小于 str2 .
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值