2021年3月

2021/3/1

  • 判断一个乘式末尾有多少个0,答案就是min(因子2的个数,因子5的个数)。
    • 即使某个乘子末尾本身就用0也不用考虑,因为10本身就是2和5的积。
  • 丑数(因数只有2,3,5)类似的题,都可以用三指针来做
    • 开始时三个指针都指向dp数组0位置,每次满足条件+1;
  • C++的set和map具有排序功能
  • 注意:在接收字符或字符串后,再接收一行字符,在此之前需要吸收到回车

2021/3/2

  • 对于联通块问题,常采用计数的方法解决
    • 此外,规模较小的可以采用深度优先遍历,规模大的采用宽度优先遍历,否则会栈溢出
  • int类型大小约为21*108这么大,4字节,32位。
  • 一般来说OJ,1秒能承受的运算次数在107~108
  • 对于多层循环,如何降低时间复杂度
    • 减少层数
    • 减少范围
    • 二分法
    • 空间换时间,提前存储好部分信息以便访问
  • 关于多个数组合成某个数的倍数的问题,考虑用根据余数分组来做。
  • 对于多括号嵌套问题先处理最小的括号,再递归

2021/3/3

  • 遇到题目说从某种初始状态到达终态最少需要多少步,这种题型,一般都是BFS宽度优先搜索来解决。
  • 定义静态二维数组时,第二维的长度须确定。
  • 所谓回溯就是
    • 执行某种行为
    • 递归
    • 撤销第一步的行为
  • 深刻体会这种DFS的不同剪枝方法
/*a个字母A,b个字母B,c个字母C,能组成多少种不同的长度位固定n的字符串*/
/*
 * 这种DFS的题目,其需要求出的是DFS能走要正确终点的路径数*/
#include <cstdio>
int DFS(int a,int b,int c,int n){   //这是我的思路,有某个字母数量还有才能递归下去
    //给出两种终止条件
    if(n==0)
        return 1;
    int res=0;
    if(a>0)
        res+=DFS(a-1,b,c,n-1);
    if(b>0)
        res+=DFS(a,b-1,c,n-1);
    if(c>0)
        res+=DFS(a,b,c-1,n-1);
    return res;
}

int cal(int a, int b, int c, int n) {   //这是答案的思路:先递归下来,如果字母数量由0变成-1,那就剪枝return 0
    //终止条件
    if (a < 0 || b < 0 || c < 0)	
        return 0;
    if (n == 0)
        return 1;
    //递归
    return cal(a - 1, b, c, n - 1) + cal(a, b - 1, c, n - 1) + cal(a, b, c - 1, n - 1);	
    //这里的返回值代表的是,选择A作为当前位置字母的话后续共有多少种方案+选择B...+选择C...
    //cal(a-1...)说明该方案选择A作为当前位置的字母,其返回值代表的是当前位置选择了A这种方案一共有多少种不同方法走到底,若下一步中判断出了a-1是<0的,那么返回值是0,相当于对这个方案做了剪枝
    
}

int main() {
    printf("%d\n",DFS(1,2,3,3));
    printf("%d",cal(1,2,3,3));
    return 0;
}

2021/3/4

  • 互质指的是两个数的最大公约数是1
  • 求最大公因数,就用辗转相除法
//递归简约版
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
  • 互质的两个数x和y,其中ax+by不能凑出的数(a、b>=0,x、y>0),最大为a*b-(a+b);
    • 意思是对大于a*b-(a+b)的数来说,任意个数的x和y一定能凑出来(a,b有非负解)
  • 计算一个长方形(长宽分别为l、w)能装下多少个边长为e的正方形
    • (l/e)*(w/e)
    • 注意这里的除是整除
  • 做竞赛题的时候务必注意数据的范围,如果数据比较大,总时间复杂度超过108就很容易执行时长超过1秒
    • 但是对于蓝桥杯这种每个测试用例单独算分的比赛来说,在没有更好的思路情况下,可以用简单的暴力法拿到不少分。
  • 对于顺序枚举的情形来说,可以使用二分法来降低时间复杂度
    • 常常在枚举优化的题目(数据超大)中体现
//二分法模板
int binarySearch(int arr[],int n,int key){
    int l=0,r=n-1;
    while(l<=r){	//注意这里必须是等号
        int mid=(l+r)/2;
        if(arr[mid]==key)
            return mid;
        if(arr[mid]<key){
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
}
  • 注意全排列next_permutation(指针1,指针2)注意指针2是开区间

2021/3/5

  • 竞赛时,在时间不够的情况下,优先考虑能多拿分的简单方法解题(如暴力法)
  • 绝对不能使用打点法来记录虚线上的实线,会产生歧义
    • 因为1,2,3,4四个点都被标记了
    • 那么是表示1~4长度为3的实线?
    • 还是表示12,34长度均为1的两条实线?
    • 解决方案:用数组记录,0号元素表示01的位置有线,1号元素表示12的位置有线
  • 对于蓝桥杯的递归填空题,搞懂参数的含义和递归的方向,基本就可以写出来(尝试写一下,然后用测试用例跑一下试试)
  • 参加比赛的时候,做题要有取舍,同时对是否暴力求解加以思索
    • 如17年的魔方状态,写几百行代码就为了求一个数,而且无法验证是否正确,不如直接跳过
  • 容斥原理
    • ∣A∪B∪C∣=∣A∣+∣B∣+∣C∣−∣A∩B∣−∣A∩C∣−∣B∩C∣+∣A∩B∩C∣
  • C++中int arr[3]={0};int arr[3]={-1};不能使用这种方法为数组初始化赋值
    • 也不能bool arr[3]={true};
    • 这样只会给第一个元素赋值

2021/3/6

  • 快速排序时间复杂度是O(nlogn)的原因是:
    • 每次确定一个元素的最终位置,然后划分两个子区间(递归划分左右子区间需要logn复杂度)
    • 每次确定一个元素的最终位置总共需要O(n)的时间复杂度
  • &是按位与
    • 如10110&1,其实质是10110&00001
  • 在处理二进制数末尾情况的时候用+1-1&
    • 如消除末尾的1,x=0011011,那么x&(x+1)=0011000
  • 10!=3,628,800, 11!=39,916,800 ,超过10的全排列基本都会超时
  • 美国人的习惯是左闭右开,所以函数参数中,基本都是左闭右开
  • 在处理一些数学问题的时候注意,程序中的除法是整除
    • 故采用a/b==c&&a%b==0;避免数学上和程序上的不一致
  • DFS递归时,递归主体中要对当前层所有情况进行处理
  • 剪枝可以大幅提升递归的效率
  • 全排列代码理解
    • 其中函数参数k,代表的是固定前k个位置的数(或者理解为固定第k个位置的数)
//递归实现全排列
#include <cstdio>
#include <algorithm>
using namespace std;
int arr[]={1,1,3};
void show(){
    for(auto a:arr)
        printf("%d ",a);
    putchar('\n');
}
//k代表的是固定第i个位置的数
void full_permunation(int k){
    if(k==3){
        show();
        return;
    }
    for(int i=k;i<3;i++){
        //这个条件加上了,能实现去重的全排列
        if(i!=k && arr[i]==arr[k])
            continue;
        swap(arr[i],arr[k]);
        full_permunation(k+1);
        swap(arr[i],arr[k]);
    }
}
int main(){
    full_permunation(0);
    return 0;
}
  • 使用递归自定义实现全排列,相对于使用next_permutation()的优点在于可以自定义剪枝(剪枝时务忘回溯),优化效率
  • OJ一般对O(108)时间复杂度,执行时间为1s
    • 13!=6,227,020,800,执行13个数的全排列大概需要1分钟左右
    • 填空题无需过度在意执行时间,此外还可以使用剪枝、二分等方法来提升执行效
  • 回溯法中,如果进行了剪枝,要及时回溯

2021/3/7

  • 判断一个数是否能开方,开方后乘方看是否与原来的数相等
    • sqrt(a)*sqrt(a)==a
  • 如何优化多层for循环的效率
    • 减少for循环层数
    • 减少每层的枚举范围
    • 二分法(对于顺序序列)
    • 缓存+查询
  • 对于某种状态都有后续两种状态的情形,可以理解为一颗二叉树
  • 问:一个字符串s最少补几个字符能把它变成对称字符串
    • 答案=s长度-lcs(s,s逆置)的长度
    • lcs最长公共子序列
    • 仔细体会其中的奥妙!

2021/3/8

  • LCS最长公共子串的dp求解方法,注意核心在于max(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main(){
    char s1[100];
    char s2[100];
    int dp[101][101];
    gets(s1);
    gets(s2);
    int len1=strlen(s1);
    int len2=strlen(s2);
    //初始化
    fill(dp[0],dp[0]+101*101,0);
    for(int i=1;i<len1+1;i++)
        for(int j=1;j<len2+1;j++){
            //计算三个值,取最大值
            int t=dp[i-1][j-1];
            if(s1[i]==s2[j])
                t+=1;
            dp[i][j]=max(max(t,dp[i][j-1]),dp[i-1][j-1]);
        }
    printf("%d",dp[len1][len2]);
    return 0;
}
  • 内存优化版本,用一维数组替换矩阵
//LCS最长公共子串的dp 一维数组写法
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main(){
    char s1[100];
    char s2[100];
    int dp[101];
    int temp[101];
    gets(s1);
    gets(s2);
    int len1=strlen(s1);
    int len2=strlen(s2);
    //初始化dp数组
    fill(dp,dp+101,0);
    fill(temp,temp+101,0);
    for(int i=0;i<len1;i++)
        for(int j=0;j<len2;j++){
            int t=dp[j];
            if(s1[i]==s2[j]) {
                t+=1;
            }
            temp[j+1]=max(max(dp[j+1],t),temp[j]);
            for(int k=0;k<len2;k++)
                dp[k+1]=temp[k+1];
        }
    printf("%d",dp[len2]);
    return 0;
}
  • Clion无法调试,使用202.2.5的版本就可以
  • 最大公因数用辗转相除法(递归版本简单,上面有代码),最小公倍数用a*b/gcd(a,b),即a和b的积除以a和b的最大公因数

2021/3/10

  • 做了一道选牌的题目,点数为1~13的牌各4张,问不考虑顺序的情况下取13张,一共有多少种组合
    • 这道题的核心在于理解什么叫不考虑顺序:即顺序不重要,每种牌取出来的张数才是重要的
    • 所以这道题就转换成了一个DFS问题,即每种牌选0~4张,总数限定13张,一共有多少种可能

回溯法

//深刻理解什么叫不考虑得到牌的先后顺序
//即第一张、第二章拿到A 和 第一张、第三张拿到A是一样的
//也就是说,重要的是A的张数
#include <cstdio>
#include <algorithm>
using namespace std;
int cards[13];
int ans=0;
int chosen=0;   //记录已经选的牌的数量

//k表示选下标为k的牌的张数
void func(int k){
    //终止条件
    if(chosen==13){
        ans++;
        return;
    }
    else if(chosen>13){
        //剪枝
        return;
    }
    if(k==13){
        //选完了还没选够13张牌
        return;
    }
    for(int i=0;i<=4;i++){
        chosen+=i;
        cards[k]-=i;
        func(k+1);
        chosen-=i;
    }

}
int main(){
    fill(cards,cards+13,4);
    func(0);
    printf("%d",ans);
    return 0;
}

非回溯法

#include <cstdio>
#include <algorithm>
using namespace std;
int cards[13];
int ans=0;
void f(int k,int c){
    //终止条件
    if(k>13 || c>13) //这里k不能=13是因为每次都是下一轮判断前面的选牌总数
        return;
    if(c==13){
        ans++;
        return;
    }
    //递归主体
    for(int i=0;i<=4;i++){
        f(k+1,c+i);
    }
}
int main(){
    fill(cards,cards+13,4);
    f(0,0);
    printf("%d",ans);
    return 0;
}
  • 有重复元素的圆排列与环排列的计数问题
    • 如何判断手链abccab是同一个手链,只是旋转了一下
    • 第一步:abc->abcabc
    • 第二步:如果cababcabc的子串,那么abccab是旋转关系
      • C++中判断子串用s1.find(s2)!=string::npos表示s2是s1的子串
    • 同理:如果加上翻转,那么就判断是否是abcabc和翻转cab的子串关系‘

2021/3/11

  • 在二维数组的两维之间来回切换

    • cur=0;
    • for循环中每轮cur=1-cur;
  • 对于结果需要对某个大数取余的题目,中间结果也需要去余,防止溢出

    • #define MOD 1000000007,注意没有;
  • 生成最小生成树的两个经典算法

    • 克鲁斯卡尔算法Kruskal

      • 将所有边从小到大排序
      • 每一轮选择最小的边加入已选边集,若形成环,则丢弃该边
      • 直到选中n-1条边
    • Prim普利姆算法

      • 本质是动态规划,使用三个数组

        • selected[n]:记录顶点是否在已选顶点集中
        • minDist[n]:记录某顶点距离已选顶点集的最短距离
        • parent[n]:记录父节点
      • 每一轮实现一个顶点数为1,2,3…n的最小生成树(动态规划)

      • 具体方法

        1. 选取0号节点加入已选顶点集,然后更新未选顶点距离已选顶点集的距离
  1. 未选顶点中选取距离已选顶点集最近的点,加入已选顶点集
    3. 更新未选顶点已选顶点集的距离
    4. 重复2、3步骤直到加入了所有节点

2021/3/12

  • 并查集可以用于克鲁斯卡尔算法中,提前判断加入当前边是否会形成环
    • 原理是判断边的两个顶点是否在一个集合中,如果已经在一个集合中,再加入该边则会形成环
    • 实现方法:寻找两个节点是否有相同的祖宗节点
    • 同一个集合中的顶点都拥有共同的祖宗节点
    • 解决实际问题的时候,需要将并查集节点和实际问题中的节点映射起来,可以采用数组的形式来映射,创建一个并查集节点数组,根据序号来映射
//实现一个并查集
#include <cstdio>
#include <set>

using namespace std;

struct UFNode {
    UFNode *parent;

    UFNode() : parent(NULL) {}
};

/**
 * 目的是找到p所指向的节点的父节点
 * 此外做了优化,将访问路径上的节点全部指向祖宗节点,这样下次查找祖宗节点时更快
 * @param p
 * @return
 */
UFNode *find(UFNode *p) {
    set<UFNode *> path;
    while (p->parent != NULL) {
        path.insert(p); //这里放入的是p指向的节点的地址,而不是p的地址
        p = p->parent;
    }
    //将路径上所有的节点,父指针都指向祖宗节点
    for (set<UFNode *>::iterator it = path.begin(); it != path.end(); it++) {
        (*it)->parent = p;
    }
    return p;
}

void merge(UFNode *p1, UFNode *p2) {
	if(find(p1)==find(p2))
        return;
    //将p2的祖宗节点变成p1的祖宗节点的父节点
    find(p1)->parent = find(p2);
}
  • 讲一个指针放入vector之中,其实质是将指针所指向的对象的地址放入vector中,而不是指针的地址
    • 同样,指针作为函数参数,其实质是临时复制一个指针,函数体内的操作,并不会改变原来指针的指向
int a=1,b=2;
int*p=&a;
vector<int*>vec;
vec.push_back(p);
p=&b;
printf("%d",*vec[0]);	//这里的结果是1

2021/3/13

  • 对于枚举过程中的重复问题
    • 存在左1右3,左3右1的情况,砍掉一个
    • 存在左2右2的情况,加上左<右的判断语句
  • C++ STL中的set和map内部就是用平衡树实现的,因此出现平衡树相关问题,可以直接使用set和map
  • KMP算法:
    • 设主串长度为N,子串长度为M,KMP算法可以将暴力求解最坏情况下O(MN)的时间复杂度降低到O(M+N)
    • 其核心思想是利用子串自身的前后缀信息,来减少暴力求解时的无用回溯
    • 如何利用next数组,见https://www.bilibili.com/video/BV18k4y1m7Ar?from=search&seid=15693083662541831658
#include <cstdio>
/**
 * 如果主串中存在子串,则返回第一次匹配时,第一个字符的下标
 * @param s     主串
 * @param sub   子串
 * @return 如果不存在,则返回-1
 */
int KMP(char s[],int len1,char sub[],int len2){
    //创建next数组
    int next[len2];
    next[0]=0;
    int i=0,j=1;
    while(j<len2){
        if(sub[j]==sub[i]){
            next[j]=i+1;    //i表示,再j位置之前的串中,其从0~i位置和末尾向匹配
            i++;        //当然我们从最大的可能性开始查找,直至i为0
            j++;
        }
        else{
            if(i==0){
                next[j]=0;
                j++;
            }
            else{
                i=next[i-1];
            }
        }
    }
    i=0,j=0;
    while(j<len2 && i<len1){
        printf("i=%d,j=%d\n",i,j);
        if(s[i]==sub[j]){
            if(j==len2-1)
                return i-len2+1;   
            i++;
            j++;
        }
        else{
            if(j==0){
                i++;
            }
            else
                j=next[j-1];
        }
    }
    return -1;
}
int main(){
    char s1[]="abxabcabcaby";
    char s2[]="abcaby";
    int ans=KMP(s1,12,s2,6);
    printf("%d",ans);
    return 0;
}

2021/3/14

  • 在可能会超时的双(多)重循环枚举中,我们可以考虑只枚举一重循环,剩下的使用二分查找(前提是有序)或者提前存储好hash表再查找的方法,来降低时间复杂度
  • 滑动窗口可以用双指针来实现
  • 蓝桥杯的规则
C/C++中怎样使用64位整数?
64位整数的类型为:long long

使用cin读的操作为:cin >> x;

使用cout写的操作为:cout << x;

使用scanf读的操作为:scanf("%l64d", &x);

使用printf写的操作为:printf("%l64d", x);
  • 只求最后四位数字,那么就%10000对一万取余
  • 通常取模运算也叫取余运算,它们返回结果都是余数 .remmod 唯一的区别在于:当 x 和 y 的正负号一样的时候,两个函数结果是等同的;当 x 和 y 的符号不同时,rem 函数结果的符号和 x 的一样,而 mod 和 y 一样。
  • &1相当于取二进制最低位数字,一般用于判断奇偶数

2021/3/15

  • 1既不是素数,也不是合数
  • 线性筛(欧拉筛)
#include <cstdio>
#include <cstring>
const int maxn=1000;    //表长
int prime[maxn],pNum=0;    //记录素数
bool p[maxn]={false};
int N;  //求N以内的素数

void f(int n){
    for(int i=2;i<=n;i++){
        if(!p[i])   //如果没有被筛掉,说明它是素数
            prime[pNum++]=i;
        for(int j=0;j<pNum;j++){
            if(i*prime[j]>n)
                break;
            p[i*prime[j]]=true;
            if(i%prime[j]==0)
                break;
        }
    }
}
int main(){
    memset(p,0,sizeof(p));
    scanf("%d",&N);
    f(N);
    for(int i=0;i<pNum;i++){
        printf("%d ",prime[i]);
    }
    return 0;
}
  • C++中对set进行排序,自定义比较函数cmp(T p1, T p2),如果按照优先级从低到高,则返回值return 优先级低的<优先级高的,其背后的原理时,sort将调换参数位置分别调用两次cmp函数,如果一次返回true,一次返回false,则认定其一个对象小另一个大,若两次都返回false,则认定两个对象相等。
    • 故这里对vector嵌套pair根据first值从小到大进行排序这样写
vector<pair<int, int> > time_and_shopID;
bool cmp(pair<int, int> p1, pair<int, int> p2) {
   	return p1.first < p2.first;
}

2021/3/17

  • 贪心的思想是,每次都选择局部最优的方案以达到全局最优
  • 对于有多种处理方式的情况,可以进行分类讨论再汇总
  • 32位有符号int型的范围是-231~231-1
    • 因为有1位要表示符号
    • 0要占用正数一个位置,所以正整数范围是1~231-1
    • 负数没有0的占位,所以负整数的范围是-231~-1
  • leetcode452题用最少量的箭引爆气球
    • 最大的体会是:一定要多画图,考虑不同的情况
  • leetcode435题无重叠区间
    • 使用贪心算法解决,核心是看区间末尾位置。贪心中,每次选择区间末尾位置最早的,就意味着为其他区间留下了最多的空间。
    • lambda表达式可以作为sort的参数(前提是C++11)
  • 在leetcode135题分糖果中,要求两侧孩子胃口要是大于中间的孩子,必须分的糖果比中间孩子多,问最少要多少糖果。遇到两侧情况这种题目,可以从左到右贪心一次,再从右到左贪心一次。
    • 注意从左往右遍历的时候,要不断利用已经更新的结果。
class Solution {
public:
    int candy(vector<int>& ratings) {
        int size=ratings.size();
        if(size<2)
            return size;
        vector<int>num(size,1);
        for(int i=1;i<size;i++){
            if(ratings[i]>ratings[i-1])
                num[i]=num[i-1]+1;
        }
        for(int i=size-1;i>0;i--){
            if(ratings[i]<ratings[i-1])
                num[i-1]=max(num[i-1],num[i]+1);
        }
        return accumulate(num.begin(),num.end(),0);
    }
};

2021/3/18

  • C++11用nullptr代替了NULL表示空指针,因为NULL在某些情况下会产生歧义
    • int*a=NULL;判断a是否为NULL/nullptr可以用if(!a)表示a是NULL
  • 写了篇Floyd判圈法的博文。

2021/3/20

  • 牛顿迭代法求平方根(保留整数,如sqrt(8)=2)
class Solution {
public:
    int mySqrt(int x) {
        long long a=x;
        while(a*a>x){
            a=(a+x/a)/2;
        }
        return a;
    }
};
  • 做题目时,一定要考虑边界情况和溢出的情况。
  • 寻找最小或最合适的区间的问题,用滑动窗口解决最优。
  • ASCII码一共128个字符,因此统计一个字符串中每个字符出现的次数,可以使用int chars[128]

2021/3/22

  • 选出数组中第K大的数
    • 先从大到小排序,再选出第K个
    • 时间复杂度更低的方法:使用选择排序或者快速排序的思想(每轮确定一个元素的最终位置 )

2021/3/26

  • 在处理一些带有棘手的边界问题和特殊情况的问题时,建议把它边界问题和特殊情况单独拿出来进行处理,不要总想着用一套通用的代码解决所有问题。
    • 比如在写K-th Element问题 ,需要用到一个辅助函数,用来确定基准元素的最终位置的。其中对i和j下标溢出的问题单独做了处理,那么就不需要那么费神来写一套能够完美覆盖溢出情况的代码了。
// 题目描述:在一个未排序的数组中,找到第 k 大的数字
class Solution {
public:

    int findKthLargest(vector<int> &nums, int k) {
        int l = 0, r = nums.size() - 1, target = r - k + 1;
        while (1) {
            int p = fix(nums, l, r);
            if (p == target)
                break;
            if (target < p) {
                r = p - 1;
            } else
                l = p + 1;
        }
        return nums[target];
    }

    //找到l位置元素的最终index,并返回
    int fix(vector<int> &nums, int l, int r) {
        int i=l,j=r;
        while(1){
            while(i<=r && nums[i]<=nums[l])
                i++;
            if(i==r+1){
                swap(nums[l],nums[r]);
                return r;
            }
            while(j>=l && nums[j]>=nums[l])
                j--;
            if(j==l-1){
                return l;
            }
            if(i>j)
                break;
            swap(nums[i],nums[j]);
        }
        swap(nums[l],nums[j]);
        return j;
    }
};

int main() {
    vector<int> vec = {10, 5, 6, 0, 0, 1, 2};
    cout << Solution().findKthLargest(vec,3)<<endl;
    return 0;
}

2021/3/27

  • 蓝桥杯只使用ANSI C/ANSI C++ 标准,不能使用C++11语法。具体则是在CodeBlocks中勾选 ISO1998标准。
    在这里插入图片描述

2021/3/29

  • 在递归中,有两种判断终止条件的写法
    • 第一种,当前轮进行判断
    • 第二种,下一轮进行判断
      • 递归中,只有满足当前层条件才能进入下一层,如果能够进入最后一层的下一层(不存在的层),则说明满足了所有层的条件。故在每一层种只需要设置否定条件,能够进入下一层即代表满足条件。
  • 排列数和组合数都可以用递归+回溯实现,其递归函数逻辑如下
//实现排列数,n个数全排列
void f(int k){
	if(k==n+1){
		生成一种排列;
		return;
	}
	for(int i=k;i<=n;i++){
		swap(nums[i], nums[k]);
		f(k+1);
		swap(nums[i], nums[k]);
	}
}
//实现组合数,从n个数种挑选m个
void f(int k){
	if(已选中的个数==m){
		生成一种组合;
		return;
	}
	if(k==n+1){
		return;
	}
	选中第k个数;
	f(k+1);
	不选第k个数;
	f(k+2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值