蓝桥杯(掌握轻松拿下省赛二等奖,自己记录的蓝桥杯知识点)

c++蓝桥杯知识

c++自带的函数库

1.find: 查找一个数组中某个元素的位置

string s;

cin>>s;

cout<<s.find('a');//find函数,寻找字符串中某个字符在某个位置,如果没有的话,那么就会出现输出的是一个非常大的数字。

2.字符串中substr的方法

substr后面的参数有两个,分别是要输出的字符串的起点,第二个是要输出的字符串的长度,例子如下:

#include<iostream> #include<string> using namespace std; int main() { string s="abcdefg"; cout<<s.substr(2,3);//输出结果为cde。 }

3.stol方法

可以将一个字符串转化成long int类型,例子如下:

#include<iostream> using namespace std; int main() { string s="1234"; int a=stol(s); cout<<a+100; }

3.erase的擦除功能

1.字符串的擦除功能

注意:在使用这个功能的时候,要先找到想要擦除的字符串的位置和长度,才能擦除。代码如下:

#include<iostream> using namespace std; int main() { string s="abcde123"; int a=s.find("12"); s.erase(a,2);//这个a是12的头地址位置,2是长度。 for(int i=0;i<s.size();i++) cout<<s[i]; }

2.容器vecotr的擦除功能

#include<iostream> #include<vector> using namespace std; int main() { vector<int> v(3); v[0]=0; v[1]=1; v[2]=3; v.erase(v.begin()+1);//擦除的是第二个。 for(int i=0;i<v.size();i++) { cout<<v[i]<<endl; } }

4.revers函数的翻转功能

string中直接使用reverse可以实现将一个字符串翻转过来,也可以单单将这个字符串中的某些字符给翻转过来。这里要特别注意:字符串的begin和end方法中,begin是有包括的,但是end是没有包括在操作里面的,意思就是说是左闭右开的。代码如下:

#include<iostream> #include<bits/stdc++.h> #include<string> using namespace std; int main() { string s="12345"; reverse(s.begin()+1,s.begin()+3);//前面那个有包括后面的没有包括。 输出的是13245.因为翻转的是从s[1]到s[3],但是由于没有包括s[3],所以答案是13245. cout<<s<<endl; }

5.unique的擦除重复元素功能

unique可以实现将重复的元素擦除,实现的原理是将重复的元素最后的那一个后面的那些元素整体往前移动,覆盖掉前面的元素,直到只要一个元素,并没有重复的元素为止,实现的代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int a[4]={1,2,3,1};
    sort(a,a+4);
    for(int i=0;i<sizeof(a)/sizeof(int);i++)
    {
        cout<<a[i]<<endl;
    }
    int len=unique(a,a+sizeof(a)/sizeof(int))-a;//unique返回的是不在这个范围的下一个元素的位置。所以,减去开头的位置,就是数组去重之后的长度加1. 
    for(int i=0;i<len;i++)
    {
        cout<<a[i]<<endl;
    }
}
6.stable_sort的排序功能。

stable_sort的语法格式和sort完全一样,除了名字不一样,然后stable_sort对比sort的优点是stable_sort会保留相同元素的相对位置。

7.count_if的累计功能。

通过count_if来实现符合条件的有几个元素,它有三个元素,第一个元素是要查询的容器的初始迭代器,第二个元素是要查询的末尾的迭代器,最后一个是要筛选的条件(函数),代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
bool compare(int a)
{
    return a<3;
}
int main()
{
    int a[4]={1,2,3,1};
    stable_sort(a,a+4);
    int len=unique(a,a+sizeof(a)/sizeof(int))-a;//unique返回的是不在这个范围的下一个元素的位置。所以,减去开头的位置,就是数组去重之后的长度加1. 
    cout<<count_if(a,a+len,compare);//判断小于3的有几个。 
}

二进制转十进制(系统自带类和函数)

在c++中,其实系统自带了将二进制转十进制的类和函数,那就是bitset这个类,使用方法如下:

#include<iostream>
#include<bitset>
using namespace std;
int main()
{
    bitset<64> b(0xffffffff);//前面 32默认全是0的,后面32位全是1的。 0x代表的是十六进制,也就是说这里的32位全都是1.
    cout<<b.to_ulong()<<endl;//将二进制转化成十进制。 
    
    //二进制
    string s="11111111";//默认是二进制 
    bitset<64> bb(s);//0x代表的是十六进制,如果是0x1111,那么说明了二进制中的十六位全都是1,也就是有十六个1。 
    cout<<bb.to_ulong()<<endl;//将二进制转化成十进制。 
    
    string str="111100011111";
    bitset<16> bitvec6(str, str.size() - 5);//最大的是在最右边的,转化成二进制是最小的位数。 数数数最后5位。 
    cout<<bitvec6.to_ulong()<<endl; 
    
    bitset<32> bitvec5(str, 5, 4);//二进制从第五位开始,数数是从左边开始数的。数4位。 
    cout<<bitvec5.to_ulong()<<endl;
}
翻转功能(flip)(这个单词的意思是翻跟斗,也就是翻转)

有两种使用方法,第一种是将某一位的数字翻转,第二种是全翻转,将所有的位都翻转,使用方法如下:

#include<iostream>
#include<bitset>
using namespace std;
int main()
{
    
    //二进制
    string s="11111111";//默认是二进制 
    bitset<16> bb(s);//0x代表的是十六进制,如果是0x1111,那么说明了二进制中的十六位全都是1,也就是有十六个1。 
    cout<<bb.to_ulong()<<endl;//将二进制转化成十进制。 
    cout<<bb.test(2)<<endl;//这个12位指的是从右到左的12位。
    cout<<bb.to_ulong()<<endl;
    cout<<bb.count()<<endl; 
    bitset<16> bbb=bb.flip(2);//flip是翻跟斗的意思,也就是翻转的意思。这句话和的意思是将倒数第二位(也就是最右边开始数,第三位开始)的翻转。 
    cout<<bbb.to_ulong()<<endl;
    
    bitset<16> bbbb=bb.flip();//将所有的位都翻转。 
    cout<<bbbb.to_ulong()<<endl;
    //要特别注意:前面运用到bb的函数时,它会直接影响bb这个变量,类似于地址的修改,是会直接影响的。
}

关于遇到很大整数的问题

当遇到的整数很大,例如2的64次方这种的,那么就要用到unsigned long long这个类型了,这个类型的可以输出很大的整数。

vector的使用

当要将一个二维数组里面的某一行一维数组赋值给另外一个二维数组的某一行时,要注意不能用v[0]=vv[0]这种最直接的赋值方式,这样是赋值不了的,应该是通过v.push_back(vv里面的某一行一维数组)这种方式来实现一维数组的复制。并且,vector容器中的长度会自动发生变化,假如pop掉一个数据,那么数组容量就会自动减1.

递归算法

例1:走楼梯(核心思想:找出表达式,在本题中是f(n)=f(n-1)+f(n-2))

你现在想爬楼梯,但是每次你只能爬一个阶梯或者两个阶梯,请当输入一个楼梯的阶梯数时,输出一共有几种爬楼梯的方法。

方法如下: f(n)=1,当n=1;

f(n)=2,当n=2;

f(n)=f(n-1)+f(n-2),当n>2.

上面这个f(n)=f(n-1)+f(n-2)就是斐波纳吉数列的思想。斐波纳吉数列的前两项是1,1.

原理如下:当你现在处于第n个阶梯时,你跨出这一步有两种方法,一种是直接跨1步,一种是跨2步,如果是跨一步的话,那么接下来就还有f(n-1)种方法,如果跨的是2步的话,那么接下来就还有f(n-2)种方法。

这个方法的实现是依靠从复杂到简单的过程,先将一个很大的数拆小,一直拆,直到等于1或者2。

万能头文件

#include<bits/stdc++.h>

数组的另外一种表达方式

数组用vector来表示,可以储存多个char的char,表达方式如下:

int num;

vector<int > s(num);

for(int i=0;i<num;i++)

{

cin>>s[i];

}

如何用vector来表示二维数组

如果想用vector来表示一个二维数组的话,那么就可以这么表示:

bool compare(const vector<int> &a,const vector<int> &b){

return a[1]<b[1];

int num;

cin>>num;

int width=2;//宽度为2,也就是说二维数组的宽度为2.

vector<vector<int> >a(num);//初始化一个行数为num的一个二维数组,宽度等下再赋予。

}

for(int i=0;i<num;i++)

{

a[i].resize(width);//这里有加i

}

for(int i=0;i<num;i++)

{

cin>>ai>>ai;

}

sort(a.begin(),a.end(),compare);进行的是二维数组中第二个数的比较,从小到大排序好。

全排序(一定是要从小到大排好序的才有用)

c++中有实现全排列的方法,如下(permutation : 排序):

#include<bits/stdc++.h> using namespace std; int main() { int a[3]={1,2,3};//这里已经是从小到大了,不然就要用sort方法来实现从小到大,不然后面的next_permutation方法会出错。 do{ cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl; }while(next_permutation(a,a+3));//从这里就可以看出这个全排列括号里面是左闭右开的,也就是说有包括左边,没有包括右边,所以在使用vector的时候就要知道:vector的end方法指向vector容器最后元素的再下一个元素(这个位置是没有元素存在的) }

这里需要提别注意的是如果这时候给的数组不是从小到大的,那么就不会把所有的情况都输出出来,例如:

a[3]={2,3,1}

那么输出的时候就是2,3,1和3,1,2和3,2,1这三种情况。

所以在使用前就需要将凌乱无序的数组从小到大排好,需要的函数有sort,这个函数,这个函数的用法如下:

sort(a,a+3);//那么就会自动的把a数组从0到2将它排好。

注意情况:如果是要让vector来实现全排列的话,那么就不能直接使用v,v+3这种类型,应该是v.begin(),v.end()这样的才可以,例子如下:

#include<iostream> #include<bits/stdc++.h> #include<vector> using namespace std; int main() { vector<int> v(3); for(int i=0;i<3;i++) v[i]=i+1; do{ for(int i=0;i<3;i++) cout<<v[i]<<" "; cout<<endl; }while(next_permutation(v.begin(),v.end())); }

排序

普通排序

调用编译器里面自带的一个函数,就是sort,这个函数会把你的数组从小到大输出出来,方法如下: a[3]={2,1,3};

sort(a,a+3);

这个时候数组a就已经自动排好序了,输出即可。

那么如何做才能实现从大到小输出呢?方法也是有的,方法如下:

bool compare(int a,int b){

return a>b;

}//要特别注意的是如果返回的是a<b的话那么排列的就是从小到大了。

a[3]={2,1,3};

sort(a,a+3,compare);//这里要注意调用这个函数的时候后面是没有加括号的。

快速排序

编译器中有快速排序的函数,函数名叫做qsort,全名是quicksort,使用方法如下:

#include<bits/stdc++.h> using namespace std; int compare(const void* a,const void* b){ return (int)(a)-(int)b; } int main() { int a[15]={1,2,3,4,5,6,7,8,9,4,3,6,4,6,1}; qsort(a,15,sizeof(a[0]),compare);//这里是15,并不是a+15; for(int i=0;i<15;i++) cout<<a[i]; }

字符串的排序

也可以用sort这个方法来执行,代码如下:

bool compare(const string &a,const string &b)

{

return a+b>b+a;//就是将字符串a和b连接起来与b和a连接起来进行字典间的比较。

}

int num;

cin>>num;

vector<string> s(num);

for(int i=0;i<num;i++)

cin>>s[i];

sort(s.begin(),s.end());//可以实现从小到大的排序。

sort(s.begin(),s.end(),compare);

类中的排序
实现类之间的排序。
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
class hashmap
{
    public:
    int begin,end;
    void insert(int a,int b)
    {
        begin=a;
        end=b;
    }
};
bool compare(hashmap h1,hashmap h2)
{
    int a=h1.end,b=h2.end;
    return a<b;
}
int main()
{
    hashmap hm[5];//自己新建的类。 
    for(int i=0;i<5;i++)
    {
        int a,b;
        cin>>a>>b;
        hm[i].insert(a,b);
    }
    sort(hm,hm+5,compare);
    for(int i=0;i<5;i++)
    {
        cout<<hm[i].end<<endl;
    }
}

unordered_map的使用

使用说明:unordered_map类似于数组,但是它是一个可以在一个单元储存两个变量的东西,这两个变量是key和value,类似于函数的x和y,是一一对应的,可以有多个value,但是只能有一个key。如果出现两个key时就会发现添加不了。

例题:找出一个字符串中第一次出现重复的字符,并且输出该字符。

代码:

#include<iostream> #include<bits/stdc++.h> #include<vector> #include <unordered_map>

using namespace std;

int main() { unordered_map<int,bool> u;//这里是无序的map,也就是说跟数组的有序排列还是不一样的。 vector<int> v(10);//别忘记在后面加上数字了。 for(int i=0;i<10;i++) cin>>v[i]; for(int i=0;i<10;i++) { if(u[v[i]]) { cout<<v[i]<<endl; break; } u[v[i]]=true; } cout<<"x"; }

快速找出两个数的最大公约数

首先用大的那个数取余小的那个数,如果值为0的话那么最大公约数就是那个小的数字,如果不为0的话,那么就用小的那个充当大的那个数,原本大的取余小的得到的数字充当小的那个数字,继续这个运算,直到运算结束。代码如下:

普通方法:

#include<iostream> using namespace std;

int gonyueshu(int a,int b)

{

if(a%b==0)

return b;

else

return gonyueshu(b,a%b);//为什么不用a/b,因为只有a%b这个数才不是b的公约数,而a-a%b的值刚好是b的整数倍。

}

int main()

{

int a=2,b=4,c;

if(a<b)

{

c=a,a=b,b=c;

}

cout<<gonyueshu(a,b);

}

进阶方法:欧几里得原理

int gcd(int a,int b){

return b==0? a:gcd(b,a%b);

}

快速找出两个数的最小公倍数

使用欧几里得原理来解决这个问题可以在面临数据量很大的时候,得以解决。

int gcd(int a,int b){

return b==0? a:gcd(b,a%b);

}

lcm(int a,int b){

return (a*b)/gcd(a,b);//最小公倍数等于两个数的乘积除以两个数的最大公约数。

}

快速找出一个数的最大公约数

思路:让这个数n,如果这个数n取余n-1不等于0的话,那么就一直减减,直到遇到一个数可以取余为0,那么就说明这个数是结果,代码如下:

#include<iostream> using namespace std;

int main()

{

int num;

cin>>num;

int a=num-1;

while(1)

{

if(num==1)

break;

if(num%a==0)

{

num=a;

break;

}

else

a--;

}

cout<<num;

}

小数点后精准度的提高

在进行小数的除法时,经常会出现一些后面有很多位的问题,所以就需要提高精准度,那么如何提高精准度呢? 在输出cout后面加上setpresion(8)//这个8指的是总的数字有6位。代码如下:

#include<iostream> #include<math.h> #include<bits/stdc++.h> using namespace std; int main() { cout<<setprecision(8)<<pow(2,20)-1<<"/"<<pow(2,19); }

输出的就是一个精度比较高的小数。

位运算& | ^的使用

如何让一个十进制的数转化成二进制后进行且,或,异或的操作。

例子:如何判断一个数字(二进制)是否是2的倍数(不能用除余的方法,因为一旦这个数大了之后就行不通了),所以方法如下: 你会发现一个数字n是2的倍数的话,那么n的二进制和n-1的二进制恰好是相反的,这时候且的话结果就是0了,只要满足这个条件就能判定该数字是2的倍数(注:&不仅仅可以取址,还可以是位运算符,表示按位与,是双目运算符,双目运算符就是说需要两个其他的东西才能使用,好比&就只是单目运算符,因为后面只需要加一个东西的名字)

关键代码如下: if(!(n&n-1))

cout<<"该数字是2的倍数"<<endl;

else

cout<<"该数字不是2的倍数"<<endl;

蓝桥杯中的日期问题

由于在蓝桥杯中十分喜欢出一些关于日期的问题,所以在这里,就给出一个解决方案,由于java自带了Calendar这个类,所以可以借助这个来解决日期的问题,方案如下:

    题目:有邪教称1999年12月31日是世界末日,当然谣言已经不攻自破。还有人称今后的某个世纪末的12月31日,如果是星期一则会....有趣的是,任何一个世纪末的年份的12月31日都不可能是星期一!!!于是"谣言制造商"又修改为星期日.......1999年12月31日是星期五,请问:未来哪一个离我们最近的一个世纪末年(即XX99年)的12月31日正好是星期天(即星期日)? 回答年份即可     
​
代码:
import java.util.Calendar;
public class Test1 {
public static void main(String[] args) {
    Calendar c=Calendar.getInstance();
    for(int i=1999;i<100000;i=i+100) {
        c.set(c.YEAR, i);
        c.set(c.MONTH, 11);//这里的12月是11,因为月份是从0开始的,一直到11,所以11代表的是12月。
        c.set(c.DAY_OF_MONTH , 31);
        if(c.get(c.DAY_OF_WEEK )==1) {//星期日是第一天。
            System.out.print(i);
            break;
        }
    }
}
}

找方格里面金币的最大值(路径最小)问题

要做出这种问题的思想不能是从最左上角一直到右下角一直找大的累加,也不能是暴力处理(列出所有的情况),会超时,那么该如何处理呢?处理的方法是迭代,如何迭代呢,过程是这样的:

因为最后的右下角要得到最大金币,所以整个过程要不是右下角的上面的那条路径最大,要么就是右下角的左边的那条路径最大,所以就要一直从左上角一直迭代到这两个数,这时候这两个数就代表了两条路径的最大金币数了,所以就需要让第一行和第一列的上面和左边都为0,在进行运算的时候才不会出错,问题以及代码如下:

问题描述

  有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。

//实现的相当于迭代,一层一层迭代,最后由两个数进行比较,哪个大结果就是哪个
#include<iostream>  
int a[1001][1001];
using namespace std;
int  main()
{
    int num;cin>>num;
for(int i=0;i<=num;i++)
{
    for(int j=0;j<=num;j++)
    {
        a[i][j]=0;
    }
}
for(int i=1;i<=num;i++)
{
    for(int j=1;j<=num;j++)
    {
        cin>>a[i][j];
    }
}
for(int i=1;i<=num;i++)
{
    for(int j=1;j<=num;j++)
    {
        if(a[i-1][j]>a[i][j-1])
        {
            a[i][j]+=a[i-1][j];
        }
        else
        a[i][j]+=a[i][j-1];
    }
}
cout<<a[num][num];
 } 

埃氏筛问题(解决质数的问题)

埃氏筛法这个的思路就是说:如果要判断一个整数是否是质数的话,假如已经知道了2是质数了,那么2+2 , 2+2+2都不是质数了。

题目:给定整数 n ,返回 所有小于非负整数 n 的质数的数量。

代码(注意:不要视图用暴力破解,会超时的):

#include<iostream> #include<vector> using namespace std; int main() { int n; int num=0; cin>>n;//判断n是不是质数。 vector<bool> v(n);//所有的都默认false。 for(int i=2;i<n;i++) { if(v[i]) continue; else { num++; for(int j=i;j<n;j=j+i) { v[j]=true; } } } cout<<num; }

贪心算法

贪心算法就是算出局部最优来得到总体最优。

二叉树的问题

1.在解决二叉树的问题的时候要特别注意端点情况,要将端点情况列出来并且放在最前面,这样才不会导致编译不过,因为你若没有把端点的情况放在最前面,那么就会导致在使用类似于root->left这个的时候报错,因为端点没有了left。

双指针解法

每次让左边的指针指向最前面的那个,右边的指针指向最后面的那个,然后进行判断,再来决定是前面的加1,还是后面的减1.例题如下:

给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

返回 承载所有人所需的最小船数 。

示例 1:

输入:people = [1,2], limit = 3 输出:1 解释:1 艘船载 (1, 2)

代码:

class Solution {

public:

int numRescueBoats(vector<int>& people, int limit) {//返回承载所有人所需的最小船数

int begin=0,end=people.size()-1;

int num=0;

sort(people.begin(),people.end());

while(begin<=end)

{

if(people[begin]+people[end]<=limit)//如果满足最轻的和最重的加起来可以做在船上,那么begin就加1

begin++;

num++;

end--;//由于每次最轻的和最重的加起来无论是否大于船的最大承载量,最重的都要上去,所以减1.

}

return num;

}

};

递归问题

如果想要让一个递归执行之后接着执行你这后面的一步,那么你就要将这两个操作同时放在同一行中,这样前面的递归彻底结束之后,接着就会来实现后面的这个递归了。

例题:

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

叶子节点 是指没有子节点的节点。

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22 输出:true 解释:等于目标和的根节点到叶节点路径如上图所示。

来源:力扣(LeetCode) 链接:. - 力扣(LeetCode) 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题方法(注意点:如果这个左结点和前面的相加之后不是否是目标值,那个如何返回上一个结点,让它进行右节点的判断 :这时候就可以让左结点和右节点放在同一行中,若是左结点的判断不满足了,那么就会结束递归,来实现右边结点的递归)。

代码

class Solution {

public:

//将两个递归的放在一起,前面的执行结束后接着就会执行后面的。

bool hasPathSum(TreeNode* root, int targetSum) {

if(!root)

return false;

else if(!root->left&&!root->right)

return targetSum==root->val;

else

return hasPathSum(root->left,targetSum-root->val)||hasPathSum(root->right,targetSum-root->val);

}

};

背包问题

0-1背包问题

在0-1背包问题中,每个物品只能拿一次,如何才能让这个有限的背包放着价值最高的东西,思路如下:

有两种解决方法,分别是一维数组和二维数组,二维数组中前面那个数字代表的是第几个物品,后面的那个代表的是背包的体积,那么如何才能拿走最有价值且最多的东西呢?

二维数组:首先就是要初始化这个二维数组,让这个二维数组成为全局变量后,就会自动初始化为0,这时候如果这个物品比当前状态的背包体积大的话,那么这个二维数组a[i][j】的值就等于a[i-1】[j】,如果这个物品比当前状态要小的话,那么就可以放到里面进去,那么这时候就还有两种情况,分别就是尽管这个物品很小,但是我就是不放进去,那么就依旧是等于a[i-1】[j】,如果想要放进去的话,那么就是等于value[i】+a[i-1】【j-v[i】】

例子和代码如下:

#include <bits/stdc++.h> ​ using namespace std; ​ int f100;//状态函数fi表示第i件物品容量为j最大价值 ​ int v[100]; ​ int w[100]; ​ /* ​ 函数功能:求完全背包 ​ 函数形参:物品数量和背包容量 ​ 函数返回值:返回最大值 ​ */ ​ int fun(int n,int m) ​ { ​ for(int i=1;i<=n;i++) //物品 ​ { ​ for(int j=1;j<=m;j++) //容量 ​ { ​ if(j<w[i]) //装不下 ​ fi=fi-1; //就等于i-1件物品容量等于j时候的价值 ​ else ​ fi=max(fi-1,fi]+v[i]);//装得下分为两种情况1.装 2.不装 ​ } ​ } ​ return fn; ​ } ​ int main() ​ { ​ int n,m; ​ cin>>n>>m; ​ for(int i=1;i<=n;i++) ​ cin>>w[i]>>v[i]; ​ cout<<fun(n,m); ​ return 0; ​ }

一维数组:一维数组和二维数组的思路极其相似,只有一小点不同而已,其中一点就是这是一个一维数组,那么每次的a[j],在经过一次外循环后就会重新赋值。我们将这个物品小于背包的内容不讨论,直接在for循环中去除掉,然后从v[i]这个开始,一直到vector这个总背包体积,来给a[j]赋值,就是让j这个背包体积得到最有效的利用,就是a[j]=max(a[j],a[j-v[i]]+value[i])

代码如下:

#include <bits/stdc++.h> using namespace std; int f[100];//状态函数f[j]表示第i件物品容量为j最大价值 int v[100]; int w[100]; /* 函数功能:求完全背包 函数形参:物品数量和背包容量 函数返回值:返回最大值 */ int fun(int n,int m) { for(int i=1;i<=n;i++) //物品 { for(int j=m;j>=w[i];j--) //容量 { f[j]=max(f[j],f[j-w[i]]+v[i]);//装得下分为两种情况1.装 2.不装 } } return f[m]; } int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) cin>>w[i]>>v[i]; cout<<fun(n,m); return 0; }

完全背包问题

完全背包问题和0-1背包问题也是十分相似的,就是不同在可能取到的物品的个数不一定为1,那么该如何解决呢?

思路和0-1背包文件基本一样,关键代码如下:resulti=max(resulti-1,resulti]+value[i])

和result[j]=max(result[j],value[i]+result[j-v[i]]),代码如下: 二维数组的:

#include<iostream> using namespace std; int v[1001],value[1001],result1001; int main() { int num,vector; cin>>num>>vector; for(int i=1;i<=num;i++) { cin>>v[i]>>value[i]; } for(int i=1;i<=num;i++) { for(int j=1;j<=vector;j++) { if(j>=v[i]) resulti=max(resulti-1,resulti]+value[i]); else resulti=resulti-1; } } cout<<resultnum; }

一维数组的: #include<iostream> using namespace std; int v[1001],value[1001],result[1001]; int main() { int num,vector; cin>>num>>vector; for(int i=1;i<=num;i++) { cin>>v[i]>>value[i]; } for(int i=1;i<=num;i++) { for(int j=v[i];j<=vector;j++) { result[j]=max(result[j],value[i]+result[j-v[i]]); } } cout<<result[vector]; }

0-1背包问题和完全背包问题用一维数组解决的不同点

0-1背包问题第二层循环是从背包容量到这个物品的容量,是从大到小的,具体如下:

for(int i=1;i<=n;i++) //物品 { for(int j=m;j>=w[i];j--) //容量 ,这个容量时从大到小的。 { f[j]=max(f[j],f[j-w[i]]+v[i]);//装得下分为两种情况1.装 2.不装 } }

完全背包问题第二层循环是从这个物品的容量到背包总容量,是从小到到的,具体如下:

for(int i=1;i<=num;i++)
{
    for(int j=v[i];j<=vector;j++)//容量时从小到大的。
    {
        result[j]=max(result[j],value[i]+result[j-v[i]]);
    }
}

经典必做大题系列

题目:

给定一个长度为 NN 的数列,A_1, A_2,⋯A-N,如果其中一段连续的子序列 A_i,A_i+1, A-i,A-i+1,⋯A*j ( i \leq jij ) 之和是 K 的倍数,我们就称这个区间 i, j 是 K 倍区间。

你能求出数列中总共有多少个 K倍区间吗?

思路:在做这道题的时候,不可以直接暴力处理,因为在暴力处理的时候,会出现数据过大的情况也就是在相加起来的时候,会出现加的数由于太多了,导致结果过大,所以得通过一些路径来解决。方法如下:从数组的开头开始一直加到最后一个数字,每加一次就将这个加的值放到另外一个数组sum中,接着在另外一个数组中储存余数从0到题目给的数字result-1的值,关键代码如下:yushu[sum[i]%result]++;得到余数从0到rusult-1的值有几个,接着遍历余数从0到result-1,随机从yushu[i]这个数组中拿出两个,也就是数学中的C(组合中随机中取出2个数)。最后将yushu[i]中遍历完后就得到结果。

代码如下:

//意思就是说在余数相同的区间,任意取两个数,这两个数之间的和就是result的倍数 #include<iostream> using namespace std; long long a[100001]; long long yushu[100001]={1}; int main() { long long num,result,sum=0; cin>>num>>result; for(int i=1;i<=num;i++) { cin>>a[i]; a[i]+=a[i-1]; yushu[a[i]%result]++; } for(int i=0;i<result;i++)//C(n个里面取2个) nn-1 /2 { sum+=(yushu[i](yushu[i]-1))/2; } cout<<sum<<endl; }

注意情况

1.数组最先开始的不能是a[n][n】,只能是a【10】【10】类似于这种,如果想要是a[n】【n】这种类型的,那么就要先让n赋值,才能使用这种类型。

2.构造函数如果想从左到右构造,而不是说让递归一直在最左边进行的话,那么就要让这个二叉树变成平衡二叉树(AVL),

这时候它就会保持二叉树的左半部分和右半部分之间是平衡的,而不会说让循环一直在左边进行,因为当左边进行到一定程度时,左右的差会大于2,这时候就要往右边构造了,就会实现从做到右。

还有一种就是比较简单也比较原始的,就是如果想让它停止的话,那么就让它的儿子全为0.

3.整型数据变换成二进制后左移或者右移,例如:int a=3;cout<<(a>>2);//意思就是说将a中的所有1都向右移动2个单位,别的情况也是这样的。如果是字符串的话,那么只要注意一点就行,剩下的和整型的一模一样,就是这个数字是否带了单引号,如果带了的话,那么这个数字就和普通的整型是不一样的,例如'7'和7,'7'相当于整型数据的55,而55和7的二进制自然是不一样的,所有左移或者右移的话都是错误的。

4.a+=3就是a=a+3,而a||=b就是a=a||b

5.任何一个二进制和1异或后结果都为相反的,因为1和1异或后就变成了0,0和1异或后就变成了1,所以就实现了将1变成0,0变成1的翻转,如果是任意一个二进制和0异或后的结果就是自己本身,同理和1异或的相似。("异或"运算符(“∧”))(这个^异或结果只为最后一位的异或)。

6.二维数组在传参数的时候,主函数中是直接将函数名传入就行,不用后面加上[]号等等情况

7.函数返回的值除了0这个数时相当于false,其他的数字都是true,包括-1等等。

8.全局数组有初始化为0,而局部数组就没有。

9.在使用vector的时候,一定要注意:如果想要直接使用a[0]=1这种类型的,那么就先要初始化vector的容量大小,不然会出现错误的,例如:vector v<int> v;//这个就是错误的。vector<int> v(10);//后面加上一个数字才是正确的。其实还有 另外一种方法:就是不用先把容器的大小固定死,就是要使用到push_back这个方法。

10.c++中函数参数传递的时候,对比引用传递和值传递,你就会发现,引用传递比值传递要快了好多,因为 c++中参数是按值传递,如果不使用引用或者指针,则这个地方每一次函数调用,string就会被复制一次,效率很低,使用引用的话函数传递的是string的引用,不会导致复制。

11.如果想利用函数来传递一个vector的话,那么要特别注意,如果想要通过这个函数来修改vector,并且在主函数中还要使用这个vector的话,那么就要在vector后面加上地址符&。

12.goto语句要注意,例如下面的这个:

int a; cin>>a; ​ if(a==1) ​ goto ya; ​ ya:cout<<"lsdfjlf"<<endl; ​ cout<<"123"<<endl;

无论a是否是1,ya这一行都是会执行的。

13.在二叉树中如果发现了编译错误,又代码找不出哪里有错误,那么大概率就是逻辑有错误,也就是可能忘记考虑端点情况了,例如空结点的时候等等情况。

14.如果想要让一个变量在循环中定义,并且只初始化一次,后面不再初始化,那么就可以用到static这个说明,用法如下:

//static的使用。 #include<iostream> using namespace std; int main() { for(int i=0;i<10;i++) { static int num=2;//这里若用的是static,那么这个变量只会初始化一次,但是若是没有加上static这个说明,那么这个变量在开始一次新的循环,就会初始化一次。 cout<<num++<<endl;//static的结果为2,3,4,5,6,7,8,9,10,11,若是没有static,那么结果就全为2. } }

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值