17.17年:
17.8:(完全背包问题)t8思路安装数学思维:如果输入的n个数的最大公约数不是是1那么他们都是质数,则凑不出的数就是无穷多个;
接着我们考虑n个数的最大公约数是1的情况,比如4,5,6这三个数,这题有点背包问题的思想利用dp数组的思维,dp[i][j]
表示前i个数是否可以凑成j(fales/ture);初始状态确定以后,后面的数一定是前面的数加4或者5或者6来的,分成了3种状态
for (int j = 0; j < 10000; ++j) {
if (f[j])f[j + a[i]] = true;
}
为什么上限定为1w,这个
t8和13年的t8买不到的数相呼应
gcd()是一个求最大公约数的函数
//求二数的最小公约数
int gcd(int a, int b) {
if (b == 0)return a;
return gcd(b, a % b);
}
17.9-分巧克力
该题的思路是搜索适合的最大边长,一步一步的进行枚举,主要计算代码如下
for (int i = 0; i < n; ++i) {
cnt += (h[i] / mid) * (w[i] / mid);
}
mid为枚举数cnt为该枚举数值下的分块个数;
为了进一步的优化,降低时间复杂度,可以利用二分法的方法去查找这个数(这里的代码是值得学习的)
17.10 -刷油漆
int abs(int i) //返回整型参数i的绝对值
double fabs(double x) //返回双精度参数x的绝对值
VC++与vs默认的栈空间是1M,超出就会发生越界
计算数组内存
如int num[900][900];
计算公式:900900sizeof(int)/1024 = 3164KB=3MB > 2MB所以溢出(VS2010下的测试)
解决办法:写成全局变量
max、min函数的头文件为:#include
17年总结:
01迷宫 暴力dfs
02跳蚱蜢 bfs
03魔方状态 模拟+判重(太难了)
04方格分割沿着中心点剪裁, dfs
05字母组串 递归,参数的含义及变化的方向
06最大公共子串 dp
07正则问题 复杂递归
08包子凑数 扩展欧几里得,完全背包问题的变体
09分巧克力 二分枚举
10油漆面积 线段树+扫描线+矩形面积
t1: 公式求组
1.数值过大时使用long long型
2.相除相减的二个数不能同时取余;相乘相加的二个数可以同时取余
3.对于公式检测是否可以简化的情况
t2: 九宫重排(练习bfs)
1.使用广度搜索模板
2.使用字符串来代表状态
3.熟悉map的特性
t3: 刷油漆
1.对于搜索题,如果深度太大,势必会超时,选择动态规划是一个比较好的选择
2.动态规划的难题是先进行题意理解如何写出状态转移方程,这往往是最难的地方,还有就是定义dp的含义
t4: 错误票据
1.int num[1000] while(cin>>num[k]) //蓝桥杯是以文件的形式输入的
2. sort(num,num+k) //数组排序
3. 该题难度不大 但题目限制条件多
t5: 翻硬币(练习bfs)
1. 需要利用广度度优先搜索算法bfs,但当数量比较大时用例通过不了(N<1000)
2. 其实吧,这道题目可以不用广度搜索,简单方法就是从头开始检查不同,不同的翻,检查完的就不用管了
t1: 枚举全部
1.bool f(int x){ //这个函数可以分解位数从而进行判断
while(x>0){
int t=x%10;
if(t2||t0||t1||t9)return 1;
x/=10;
}
return 0;
}
t2: 取模对加乘没有影响
2:编程填空题 直接复制代码 填了空测试 所填代码是否正确,可能没有思路 可以尝试一下手算
3:有的题写不出来最优的算法,也要要暴力的算法写出来,过部分数据
4:编程题 有大量相同的代码块时 直接复制粘贴 不要手打 节约时间
5:填空题直接暴力枚举,求答案
6:蓝桥杯很多题都是可以通过枚举排列,枚举子集来得出答案
7:写编程题先审清楚好题意,判断时间复杂度和空间复杂度在写。没把握就先别写,免得浪费时间。
8: 万能头文件(#include
9: 能用到函数的地方尽量用函数,比如sort、next_permutation等,省时省力,STL同理。
10: 代码填空:一般是递归、找规律等,实在想不出来可以随便试,很容易试对
11: 有一句话叫“如果询问的两点不连通则输出-1”,这句话隐含意义就是后台至少有一组测试样例是不连通的。
那与其空着不写,不如你就写个程序输出-1,假如后台有5组测试样例,你也拿到了1/5的分数。
特性: 顺序容器,vector 容器与数组相比其优点在于它能够根据需要随时自动调整自身的大小以便容下所要放入的元素。
此外, vector 也提供了许多的方法来对自身进行操作。
头文件:
#include
常用函数:
#声明
vector<int> a ; //声明一个int型向量a
vector<int> a(10) ; //声明一个初始大小为10的向量
vector<int> a(10, 1) ; //声明一个初始大小为10且初始值都为1的向量
vector<int> b(a) ; //声明并用向量a初始化向量b
vector<int> b(a.begin(), a.begin()+3) ; //将a向量中从第0个到第2个(共3个)作为向量b的初始值
#基本操作
a.size() //获取向量中的元素个数
a.empty() //判断向量是否为空
a.clear() //清空向量中的元素
a.push_back(c); 尾部插入数字:
c.reserve(100); 定义空间为100
cout<<a[0]<<endl; 使用下标访问元素,记住下标是从0开始的。
使用迭代器访问元素:
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
插入:
vec.insert(vec.begin()+i,a); 插入元素,在第i+1个元素前面插入a;
vec.erase(vec.begin()+2); 删除元素,删除第3个元素
vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始
排序:
sort(vec.begin(),vec.end());
反转:
reverse(vec.begin(),vec.end());
2.map
头文件:
#include
特性:
C++中map提供的是一种键值对容器,里面的数据都是成对出现的,每一对中的第一个值称之为关键字(key),第二个称之为该关键字的对
应值Values。每个关键字只能在map中出现一次。具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。
常用函数:
声明: map<int, string> ID_Name;
map<int, string> ID_Name = {
{ 2015, "Jim" },
{ 2016, "Tom" },
{ 2017, "Bob" } };
迭代器声明:map<int,int>::iterator it;
插入函数:
// 定义一个map对象
map<int, string> mapStudent;
// 第一种 用insert函數插入pair
mapStudent.insert(pair<int, string>(000, "student_zero"));
// 第二种 用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type(001, "student_one"));
// 第三种 用"array"方式插入
mapStudent[123] = "student_first";
mapStudent[456] = "student_second";
///insert操作是不能在插入数据的,但是用数组方式就不同了,它可以覆盖以前该关键字对 应的值,
查找函数:
// find 返回迭代器指向当前查找元素的位置否则返回map::end()位置
iter = mapStudent.find("123");
if(iter != mapStudent.end())
cout<<"Find, the value is"<<iter->second<<endl;
else
cout<<"Do not Find"<<endl
删除与清空元素:
//迭代器刪除
iter = mapStudent.find("123");
mapStudent.erase(iter);
//用关键字刪除
int n = mapStudent.erase("123"); //如果刪除了會返回1,否則返回0
//用迭代器范围刪除 : 把整个map清空
mapStudent.erase(mapStudent.begin(), mapStudent.end());
//等同于mapStudent.clear()
// 查询map是否为空
bool empty();
// 查询map中键值对的数量
size_t size();
// 清空map,清空后的size为0
void clear();
erase() 删除一个元素
find() 查找一个元素
map.count(Key) 返回值为1或者0,1返回存在,0返回不存在,返回的是布尔类型的值,
特性:它只允许在表的前端进行删除操作,只允许在表的后端进行插入操作;
头文件:#include <queue>
定义一个queue的变量 queue<Type> M
查看是否为空范例 M.empty() 是的话返回1,不是返回0;
从已有元素后面增加元素 M.push()
输出现有元素的个数 M.size() 返回个数
显示第一个元素 M.front()
显示最后一个元素 M.back()
清除第一个元素 M.pop()
deque 容器也擅长在序列尾部添加或删除元素(时间复杂度为O(1)),而不擅长在序列中间添加或删除元素。
deque 容器也可以根据需要修改自身的容量和大小。
sort 函数:
bool complare(int a,int b) {
return a>b;
}
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10, complare);
swap 函数:
int a=1, b=2;
swap(a,b);//实参传入
最值:
int a=1, b=2;
cout<<max(a,b); // 输出 2
cout<<min(a,b); // 输出 1
绝对值:
abs(x) //返回x的绝对值值 必须为int型
元素反转:
reverse(it1,it2)//将数组指针在[it1,it2)之间的元素或容器的迭代器在[it1,it2)范围内的元素进行反转。
全排列:
可对字符串全排序:next_permutation(str.begin(),str.end())
对数组全排序:next_permutation(a,a+n) //a[n]
//输出n=3的全排列
int main(){
int a[10] = {1, 2, 3};
do{
printf("%d%d%d",a[0], a[1], a[2]);
}while(next_permutation(a, a+3));
}
5.memset
头文件: #include<string.h>
解析: void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符
使用:
二维数组:
int test[5][5];
memset(test, 0, sizeof(test));
memset(&test[0][0], 0, sizeof(test));
一维数组:
int a[100];
快速初始化数组元素全部为0
memset(a,0,100 *sizeof(int));
头文件:#include
特性:各键值对的键和值对应相等,自行根据键的大小对存储的键值对进行排序,使用 set 容器存储的各个元素的值必须各不相同
最正确的修改 set 容器中元素值的做法是:先删除该元素,然后再添加一个修改后的元素。
使用:
初始化:std::set<std::string> myset{"http://c.biancheng.net/java/",
"http://c.biancheng.net/stl/",
"http://c.biancheng.net/python/"};
插入://向myset容器中插入新元素
myset.insert("http://c.biancheng.net/java/");
迭代器: //利用双向迭代器,遍历myset
for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
cout << *iter << endl;
}
头文件:#include
特性:
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的
地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存
在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
在从大到小的排序数组中,重载lower_bound()和upper_bound()
lower_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到
返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater<type>() ):从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回
该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
pow()函数的头文件是#include<cmath>
#include 库中的:
//读取一行文件流
string line;
getline(cin, line);
//格式化读取字符串
int h1, m1, s1, h2, m2, s2, d;
sscanf(line.c_str(), "%d:%d:%d %d:%d:%d (+%d)", &h1, &m1, &s1, &h2, &m2, &s2, &d);
%02d//%02d的意思是输出二位不足的自动补0;
cin.get();或者 getchar();//滤去上次的回车键
语法:
const char *c_str();
c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同.
这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针
比如:最好不要这样:
char* c;
string s="1234";
c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时,编译器也将报错——将一个const char *赋与一个char *。
应该这样用:
char c[20];
string s="1234";
strcpy(c,s.c_str());
这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作
再举个例子
c_str() 以 char* 形式传回 string 内含字符串
如果一个函数要求char*参数,可以使用c_str()方法:
string s = "Hello World!";
printf("%s", s.c_str()); //输出 "Hello World!"
算法:
现在学了dfs、bfs、并查集、d算法、动态规划、