算法竞赛小技巧总结笔记(持续更新中~~~)

$.二维前缀和预处理:s[i][j] = s[i - 1][j] + s[i][j - 1] + a[i][j] - s[i - 1][j - 1];
   二维前缀和结论:  子矩阵的和 = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] 

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.判断题目能否用二分来做:
  1.判断答案是否有单调性? => 有单调性一定可以用二分法。
  2.判断是否具有二段性? => 没有单调性有二段性也可以用二分法。(有单调性一定有二段性)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.判断是否超空间: 1.代码中一共开辟了多少byte?  int/char/double => 4/1/8 byte
                              2.1024*1024byte == 1MB , 与题目限制的MB数比较即可。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.经典数学定理:

引理:给定a,b 若d=gcd(a,b)>1 则一定不能凑出最大数
 结论:如果 a,ba,b 均是正整数且互质,那么由 ax+by,x≥0,y≥0  不能凑出的最大数是 (a−1)(b−1)−1

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.输出正余数: int get_mod(int a, int b)   // 求a除以b的正余数
                      {
                           return (a % b + b) % b;
                       }   

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.运用前缀和查找数值边界: (构建“计数数组”的前缀和)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.将一个数的所有位数提取出来:                             将字符串转换为整数:
                             while(x)                                        for(int i = 0; i < str.size(); i ++)
                             {                                                   x = x * 10 + str[i] - '0';
                                 int t = x % 10;                                  
                                 x /= 10;
                                 .....
                             }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.求下取整:#include ------ ceil((double)a / b);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.求整数的位数:len = to_string(a}.size()

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.$.回文日期:(枚举回文数,再判断是否合法) 1.整个八位数构成的日期是否合法;2.是否在范围内.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.Foold Fill算法:1.DFS(深搜)更好写代码,方便。
                             2.BFS(宽搜)需要额外写一个队列,但是可以求出最短路.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.置换群算法(AcWing.1224 交换瓶子):

   结论:初始状态可构建k个环,目标状态可构建n个环,所以至少操作n - k次。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.1233求岛屿沉没数:

核心思想是统计每个岛屿上海边陆地块数bound与所有陆地块数total,

若bound == total 则此岛屿将会沉没

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.经典结论:

104.货仓选址(一维) 当起点距离各个仓库距离之和最小时:起点应该在中位数or中间两个数之间(用绝对值不等式证明)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.快速选择函数O(n) :

位于#include中的nth_element(所选的元素的位置, 最后一个迭代器后面的位置);
 eg:nth_element(a, a + n / 2, a + n) 可求中位数

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.区间问题就是排序,or是按左端点排序 or是按右端点排序 or是双关键字排序.
 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.构建小根堆语法: priority_queue, greater> heap;  

(#include

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.用最少的区间覆盖一定区域核心思想:

1.将所有区间按照左端点从小到大进行排序
2 .从前往后枚举每个区间,在所有能覆盖start的区间中,选择右端点的最大区间,然后将start更新成右端点的最大值。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing.148合并果子:(贪心,哈夫曼树,堆,优先队列) O(nlogn)  

经典哈夫曼树的模型,每次合并重量最小的两堆果子即可。
使用小根堆维护所有果子,每次弹出堆顶的两堆果子,并将其合并,合并之后将两堆重量之和再次插入小根堆中。每次操作会将果子的堆数减一,一共操作n - 1次即可将所有果子合并成1堆

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.125.叠罗汉的牛: (经验公式)w[i+1]+s[i+1]>=w[i]+s[i]
                              即当上面的牛重量和强壮度之和大于下方时,上下交换.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.多重背包问题:

所有物品都拆开(一个个拆可能会超时) 故使用二进制数优化 ( !一个数x需要拆成logx份!)
例子:要求在一堆苹果选出n个苹果。我们传统的思维是一个一个地去选,选够n个苹果就停止。这样选择的次数就是n次.
 二进制优化思维就是:现在给出一堆苹果和10个箱子,选出n个苹果。将这一堆苹果分别按照1.2.4.8.16....512分到10个箱子里任何一个数字[0,1023]都可以用箱子表示出来,因此选择的次数为10次

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.896.最长上升子序列II :

栈【不用于记录最终的最长子序列】,而是【以stk[i]结尾的子串长度最长为i】
或者说【长度为i的递增子串中,末尾元素最小的是stk[i]】。
理解了这个问题以后就知道为什么新进来的元素要不就在末尾增加,要不就替代第一个大于等于它元素的位置。
这里的【替换】就蕴含了一个贪心的思想,对于同样长度的子串,我当然希望它的末端越小越好,这样以后我也有更多机会拓展。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。  lower_bound(stk.begin(), stk.end(), arr[i])

                    upper_bound(stk.begin(), stk.end(), arr[i])

                    该函数用于在指定区域内查找不小于目标值的第一个元素。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing 898. 数字三角形 状态表示:f[i, j]表示所有从(1, 1)到(i, j)所有方案的集合的最小值

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing 895. 最长上升子序列
f[i]表示从第一个数字开始算,以w[i]结尾的最大的上升序列。(以w[i]结尾的所有上升序列中属性为最大值的那一个)
状态计算(集合划分):j∈(0,1,2,..,i-1), 在w[i] > w[j]时,f[i] = max(f[i], f[j] + 1)。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing 897. 最长公共子序列 f[i][j]表示a的前i个字母,和b的前j个字母的最长公共子序列长度

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing 902. 最短编辑距离 f[i][j] = 所有将a[1~i]变成b[1~j]的操作方式

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.当状态转移方程出现i - 1时,字符串从1开始读入.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.AcWing 282. 石子合并f[i][j]表示将i到j这一段石子合并成一堆的方案的集合,属性 Min  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.区间DP问题:
              所有的区间dp问题枚举时,第一维通常是枚举区间长度,并且一般 len = 1 时用来初始化,枚举从 len = 2 开始;
              第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1)
                for (int len = 1; len <= n; len++) {         // 区间长度
                    for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
                        int j = i + len - 1;                 // 区间终点
                        if (len == 1) {
                            dp[i][j] = 初始值
                            continue;
                        }
                        for (int k = i; k < j; k++)       // 枚举分割点,构造状态转移方程
                            dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.只需要求值不需要修改时可以利用一个数组既可以存数据又可以求前缀和.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.计数类DP: 900. 整数划分

思路:把1,2,3, … n分别看做n个物体的体积,这n个物体均无使用次数限制,
           问恰好能装满总体积为n的背包的总方案数(完全背包问题变形)
                                
          状态表示: f[i][j]表示只从1~i中选,且总和等于j的方案数

          状态转移方程: f[i][j] = f[i - 1][j] + f[i][j - i];


                其他算法
                          状态表示:f[i][j]表示总和为i,总个数为j的方案数

                          状态转移方程: f[i][j] = f[i - 1][j - 1] + f[i - j][j];

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.如果是读入多组数据,要记得每次循环开始把使用过的数组清空。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.C++位运算的优先级比加减乘除的优先级低,所以遇到位运算和加减乘除一起的,要加个括号。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.树状数组:用于单点修改和区间查询。 配合差分可以解决区间修改和单点查询。
           背: c[x] = (x - lowbit(x), x)
 三个核心函数: int lowbit(int x)         void add(int x, int v)
                          {                               {
                                    return x & -x;            for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
                          }                                }
            
                int query(int x)
                {   
                    int res = 0;
                    for(int i = x; i ; i -= lowbit(i)) res += tr[i];
                    return res;
                }
                    

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.线段树:1.单点修改 2.区间查询 (都是logn)  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.各进制数转化为十进制方法:

1.X进制的数,各位数字的进制是相同的,每位数字的权重是X^n(n为该位数字后面的位数),
   比如二进制1000中首位的1的权重是2^3=8;转化为十进制就是各个位数的权重相加。
   但也有可能各位数字进制不同,该位数字的权重其实是后面各位数字的进制的乘积。       

2.最小进制不能小于2;而对于X进制的数来说,合法的最大数字是X-1

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.倒序输入数字:scanf("%d", &m1);
                            for (int i = m1 - 1; i >= 0; i -- ) scanf("%d", &a[i]);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.滚动数组优化通法: 1.将状态数组第一维大小开成2  2.将公式中第一维都 &1

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.1050.将M分成N个数的和:f[i][j] = 所有总和是i且分成j个数的所有方案,

  划分方式:最小值是0和最小值不是0.(第一次遇见非常难以想到)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.C++语法特性:&a = f[x]; => 后续 写a = f[x].

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.循环条件中一定不能用函数,否则会很慢。需要用根号时用i <= n / i;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.把一个数N 写成:N = (p1^x1^)(p^x2)(p3^x3)…(pk^xk),其中pi为质数。则N的约数个数为:(x1+1)(x2+1)(x3+1)…(xk+1)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.定理2(算数基本定理): 任何一个大于 1 数都能分解为素数幂次方乘积的形式.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.dijkstra: int dist[n],state[n];
                dist[1] = 0, state[1] = 1;
                for(i:1 ~ n)
                {
                    t <- 没有确定最短路径的节点中距离源点最近的点;
                    state[t] = 1;
                    更新 dist;
                }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.松弛操作,就是看一看distv和distu+u到v的距离哪个大一点。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.bellman - ford算法擅长解决有边数限制的最短路问题
                    for n次
                        for 所有边 a,b,w (松弛操作)
                            dist[b] = min(dist[b],back[a] + w)
                            
    是否能到达n号点的判断中需要进行if(dist[n] > INF/2)判断,而并非是if(dist[n] == INF)判断,
    原因是INF是一个确定的值,并非真正的无穷大,会随着其他数值而受到影响,dist[n]大于某个与INF相同数量级的数即可

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.spfa算法文字说明:1.建立一个队列,初始时队列里只有起始点。

                                    2.建立一个数组dist记录起始点到所有点的最短路径(该表格的初始值要赋                                         为极大值,该点到他本身的路径赋为0)。

                                    3.再建立一个数组,标记点是否在队列中。

                                    4.队头不断出队,计算始点起点经过队头到其他点的距离是否变短,
                                如果变短且被点不在队列中,则把该点加入到队尾。重复执行直到队列为空。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.涉及拆分位数的时候可能用string更简单.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.如果n可能为奇数的话不能出现n / 2.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.判断一个数是否为整数:  if((int)x==x)  

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.1MB = 1024KB = (1024 * 1024) B = ((1024 * 1024) * 8)b 其中B = byte( 1byte也叫1字节 ) , b = bit( 1 bit也叫一位)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.判断浮点数是否相等时要注意误差,当两个数绝对值只差 < 1e8 时就可以认为两者相等.    if (fabs(a - b) > 1e-8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.快速提取一个数的每一位:for(auto c : to_string(x))  {...}      (先变为字符串再提取每一位)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$平均下来每个数约数个数大约为logn个(10^6以内720720的约数最多有240个,int内最多一个数有1600左右个约数)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.当需要多次操作数组时需要备份: memcpy(backup, g, sizeof g);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.g[a][b] ^= 1;   //异或,不同的时候就变成相反的数

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.组合型枚举不需考虑顺序:故引用state保证其选数递增

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.next_permutation()全排列函数

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.因为C++中除法是整除,所以要转化为加减乘来计算

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.  把数组转化为数: for(int i = l; i <= r; i ++) res = res * 10 + num[i];

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$多行字符矩阵的快速读入:

for(int i=0; i>s; for(int j=0; j

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.快速幂算法模板

int qmi(int m, int k, int p)
{
    int res = 1 % p, t = m;
    while (k)
    {
        if (k&1) res = res * t % p;
        t = t * t % p;
        k >>= 1;
    }
    return res;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.string 转 int:

                        int num = stoi(s) 

                        long num = stol(s);

                        long long num = stoll(s);

                        double num = stod(s)

$0int 转 string:     这个就 直接 string num = to_string(num) 


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.字符的次序问题(哈希)

  给26个字母附上顺序解决字符谁先谁后的问题
for (int i = 1; i <= 26; i++) { 
     char ch; 
     cin >> ch;
     a[ch - 'a'] = i;
 }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.pair的输出

        for (auto it = nums.begin();it != nums.end();it++) 

                        cout << it.first << it.second ;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.字符串中固定字符的数量
    string temp = "aaabcdaaa!!!";
    int num = count(temp.begin(),temp.end(),'a');
    cout <<"在字符串" << temp << "中," <<"字母a出现的次数是" << num << endl;
    return 0 ;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.关于填空题

1.要巧用word等工具

2.可以眼看手数

3.巧用Excel

4.巧用计算器(可以计算日期)

5.巧用python:

(1)用Python算大数

         eg: 给100个整数,问它们乘积的末尾有多少个零

         
num=[int(i) for i in input().split()] 
# input().split()
s = 1
cnt = 0
for i in range(len(num)): 
    s *= num[i]             
    while s%10 == 0:        
       s /= 10             
       cnt += 1 
print(cnt)

 

                          (2)用Python处理字符


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

$.题目的样例只有20%的情况下x<10,可以写上特判骗分。       

if(x==1)cout<<5< if(x==2)cout<<7< .......
                     
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

$.++ STL中 next_permutation函数的用法

固定用法:常常与do while语句联用

(为什么不直接while?:初始状态往往也是一种情况)             

1、输出序列{1,2,3,4}字典序的全排列。

#include
#include
using namespace std;
 
int main(int argc, char** argv) {
    int a[4]={1,2,3,4};
    sort(a,a+4);
    do{
        //cout<         for(int i=0;i<4;i++)
            cout<         cout<     }while(next_permutation(a,a+4));
    return 0;
}

2、输入任意一个字符串,输出其字典序的全排列

#include
#include
using namespace std;
 
int main(int argc, char** argv) {
    string str;
    cin>>str;
    sort(str.begin(),str.end());
    do{
        cout<     }while(next_permutation(str.begin(),str.end()));
    return 0;
}

3、能否直接算出集合{1, 2, ..., m}的第n个排列?
举例说明:如7个数的集合为{1, 2, 3, 4, 5, 6, 7},要求出第n=1654个排列。

#include
#include
using namespace std;
 
int main(int argc, char** argv) {
    int a[7]={1,2,3,4,5,6,7};
    sort(a,a+7); 
    int n=0;
    do{
        if(n==1654){
            for(int i=0;i<7;i++)
              cout<             cout<         break;
        }
        n++;
    }while(next_permutation(a,a+7));
    return 0;
}

    

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.进制转换

1.strtol函数

它的功能是将一个任意1-36进制数转化为10进制数传入字符串,返回是long int型

函数为long int strtol(const char *nptr, char **endptr, int base)

2.itoa函数:

它的功能是将一个10进制的数转化为n进制的值、其返回值为char型。(和上面的strtol效果相反)

例如:itoa(num, str, 2); num是一个int型的,是要转化的10进制数,str是转化结果,后面的值为目标进制。


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

$.文件读取

freopen("C:\\Users\\张px\\Desktop\\input.txt","r",stdin);


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

$.判断日期是否合法模板

bool check(int year, int month, int day)
{
    if(!month || month >= 13) return false;
    if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) months[2] = 29;
    if(day > months[month] || day <= 0) return false;
    months[2] = 28;
    return true;

先判断月是否合法,在判断每月天数

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

$.res = res / i * (i - 1) 先写除法再写乘法防止中间结果溢出

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

$.有限制的选择物品问题 == 背包问题

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

$杨辉三角

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 $.欧拉函数递推式(非常好用)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

模运算的性质: (a * b) % p = (a % p * b % p) % p

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.边上存的信息是很灵活的,除了存边权,还能存各种会影响更新的其他信息

                                        a–(w,b)–>c , b–(w,a)–>c

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.自定义sort函数(等同于cmp函数与运算符重载)

sort(range, range + n, [&](Range a, Range b)
    {
        return a.r < b.r;
    });

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int months[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool check(string str)
{
    for(int i = 0; i + 2 < str.size(); i ++)
        if(str[i + 1] == str[i] + 1 && str[i + 2] == str[i] + 2)
            return true;
}
int main()
{
    int year = 2022, month = 1, day = 1;
    string str[10];
    for(int i = 1; i <= 365; i ++)
    {
        sprintf(str, "%04d%04d%04d", year, months, day);
        if(check(str)) 
        {
            res ++;
            cout << str << endl;
        }
        if(day = months[month])
        {
            day = 1;
            month ++;
        }
        day ++;
    }
    cout << res;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.进制数的本质

X进制的数,各位数字的进制是相同的,每位数字的权重是X^n(n为该位数字后面的位数),比如二进制1000中首位的1的权重是2^3=8;

若各位数字进制不同,该位数字的权重其实是后面各位数字的进制的乘积。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  $. for(int i = 0;i < 2;i ++ )
    {
        string a;
        getline(cin,a); // 读取带空格的字符串,整行读取
        s += a;
    }

不读取空格:while(cin >> c) str += c;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

$.打表

在一些数据范围小,答案可以枚举,且时间要求上较为苛刻的题目中,我们可以采用暴力枚举的方法得出答案,然后将答案存入数组中,然后根据输入直接输出答案。这样一种方法我们称之为打表。(打表首先应该满足正确性而非效率)

需要用到ostream库,其中内部的freopen函数用来打开/新建文档,我们将答案输出到文档中。
 

#include 
#include 
using namespace std;
int sum=0;//计数器
void cnt(int n)
{
    sum++;
    for(int i=1;i<=n/2;i++)
        cnt(i);
}
int main()
{
	freopen("data.out","w",stdout);
	int n;
	for(n=1;n<=1000;n++)//计算每个结果
	{
		sum=0;//初始化计数变量 
		cnt(n);
		cout<

你可能感兴趣的:(算法)