输入与输出
- scanf函数的:
int a,b;
scanf("%d %d",&a,&b);
float c;
double d;
scanf("%f %lf",&c,&d);
类似的代码不管两个%d中间是空字符串,还是许多个空格,在输入的时候均可直接用一个空格或者换行符进行分隔
- scanf函数读取单个字符存入字符数组:
scanf("%c",&a[k]);
不管是空格还是换行符,均会读取,所以此时字符数组存储的东西是不完全的,可以在后面加上getchar()消除空格或换行
- 特别注意
当遇到类似的输入的时候
6
A
B
C
D
B
C
第一个表示接下来要输入的字符行数,我们想要把下面的几个存入字符数组,应该在读取数字的scanf后面再加一个getchar!!!
int length;
scanf("%d",&length);
getchar();//这个不可以少!!!
for(int i=0;i<length;i++){
scanf("%c",&cows[i]);
getchar();//这个当然得加
}
- gets()函数
gets()函数用来从标准输入设备(键盘)读取字符串直到换行符结束,但换行符会被丢弃,然后在末尾添加’\0’字符。其调用格式为:
char s[100];
gets(s);
其中s为字符串变量(字符串数组名或字符串指针)。
gets(s)函数与scanf("%s",s)相似,但不完全相同,使用scanf("%s",s) 函数输入字符串时存在一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。
-
putchar()函数
用于输出单个字符 -
string 与 char *
- 当我们定义了一个string,就不能用scanf("%s",s)和printf("%s",s)输入输出。主要是因为%s要求后面是对象的首地址!!! 也就是char *
- string 转 char*
include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
string s="xiaoming";
const char *a=s.c_str();//去掉const会显示编译错误
const char *b=s.data();
printf("a:[%s],b:[%s]\n",a,b);
}
- char* 转 string
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
char *a="xiaoming";
string s;
s=a;
printf("%s\n",s.c_str());
}
- 输出函数printf
long long b=121;
double a=121.31;
printf("%lld %lf",b,a);
关于PE或WA
- 注意换行符号
- 注意最终的结果输出如果很大的话是否有换行要求,总之仔细审题!
- 有可能是让每个元素占3个位而不是每个元素之间俩空格!使用%3d进行输出
- 可能是因为读入字符的时候读入了空格,可以考虑换成cin>>进行字符的读取
- 可能需要用到高精度,或者long long
- 看看是不是对于0之类的结果设置的没有相应的输出
- sort函数在自定义比较函数的时候如果两个等价,最好是返回false
- 对于两个int类型a和b,他们的商如果赋值给double,必须进行强制类型转换
,否则将把一个int类型的结果赋值给double,这个商小数位是0的!
枚举
1. 可以重复的全排列
使用两个循环即可
2. 不可以重复的全排列
2.1 如果是n个元素,排列n个位置
使用如下的函数next_permutation(),如果有n个元素,那么有n!种情况
char a[4]={'a','b','c','d'};
do{
printf("%c %c %c %c\n",a[0],a[1],a[2],a[3]);
}while(next_permutation(a,a+3));
//还有prev_permutation()
2.2 如果是n个元素,排列k(k<n)个位置
也可以使用上面的next_permutation()但是需要除以(n-k)!
3. 组合,Cn^k类的问题
3.1 借助排列函数
使用上面的next_permutation(),但是需要除以(n-k)!*k!
3.2 使用0/1和next_permutation
查看另一篇博客即可
4. 枚举每一种可能之类的问题
比如a,b,c,d四个元素,可能取0个,可能取1个,直到可能全部都取
这里可以借助位运算符实现枚举
总共n个元素,那么一定有2^n种可能性,可以为1<<n,对于每一个数字,都进行>>x&1运算即可存入数组,表示枚举的结果
基本转换
- 字符串转数字
int main(){
int a;float b;long c;
a=atoi("321");
b=atof("3.14125");
c=atol("56722183");
//如果是字符串s,需要s.data()
printf ("%d,%f,%d",a,b,c);
return 0;
}
- 数字转字符串
int main(){
char a[10],b[10],c[10];
sprintf(a, "%d", 32);
sprintf(b, "%f", 3.1415);
sprintf(c, "%d", 567283);
string str(a);
puts(a);
printf("%s\n%s\n%s\n",a,b,c);
return 0;
}
- 进制转换
//函数原型:
char*itoa(int value,char*string,int radix);
- strlen(char *)
用于计算字符数组的长度,遇到\0就结束了
初始化
- 全局变量,初始化默认为0或空字符串
- 如果将大数组进行初始化为-1,使用memset
#include<memory.h>
using namespace std;
int a[100][100];
int main(){
memset(a,-1,sizeof(a));
printf("%d",a[1][2]);
return 0;
}
因为是按照字节进行初始化的,所以最好要么是-1(每一字节是11111111)或者0,如果是初始化为1,那么上面数组中每个元素都是16843009,因为(00000001 00000001 00000001 00000001=16843009)
3. memset INF
-
0x3f3f3f3f的十进制是1061109567,也就是10^ 9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
-
另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
-
最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a))这样的代码来实现(方便而高效),但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0,现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))
-
const int INF=0x3f3f3f3f
- memcpy
- void * memcpy(void *dst, void *src, int length);
- 其功能为将src地址上长度为length字节的数据,复制到dst上
- 优化
#pragma GCC optimize(2)
- 如果数据太大
最保险的方式是将所有数值设为long long
long long ans=50000*50000;
//最终结果为负数,这就是数据不全设置为long long 造成的隐患!
搜索方面注意的问题
dfs
- 设置终止条件
- 设置当前状态
- 设置下一个搜索的循环
对于当前的状态,仔细考虑如果出栈之后到底是清除状态还是保留已搜索状态,这对程序的运行时间有重要影响
bfs
- 队列记得清空
- use表记得清空,记得刚把元素放入队列就更新,特别是第一个
- 记得设置flag,万一循环完之后也没结果这种情况要考虑
逗比错误集合
- 判断质数的时候0 <i <sqrt(n) 应该是<=
- 写并查集合并结点只合并子节点
- 忘了清空栈,队列,容器
- 没注意题目输出要求到80个元素就换行
- 在for循环里初始化后再赋值,应该在外面初始化
- 审题错了,想当然以为是从每个路径中最长边中取最小,结果应该是从每个路径中最短边中取最大
- 全局变量有一个为double结果定义成了int,结果导致vector莫名其妙的size变得很大
- 把不同的变量意义看反了,计算的时候变量位置错了
- 为了让图的i,j对齐,让输入的from,to 都减了1,但是把cost也给减了1
- 循环里满足条件之后忘了加break
- 取模的时候没有考虑周全
- 以后for循环都给我他妈的打括号
最短路
- 对于最短路的解法:对于每一个点来说,都会有一个值,我每次循环都是尽可能让连接我的点的值加上边的权重最小,也就是说
不断的更新d[to]=min(d[to],d[from]+cost)
- 对于压路机问题,对于每一个点来说,是让自己的值尽量为连接我的点与边的权重之间的最大值,因为肯定是能连上的,所以就尽可能多搬一些重量,也就是说
不断的更新d[to]=max(d[to],min(d[from],cost))
- 对于d[from],意味着我是以最大的代价成功抵达的,但是我要向到达d[to],如果cost比我大还好说,听我的,要是cost比我小,我就得听cost的
- 对于d[to]来说,既然能够连接到我,我自然得选择代价最大的
- 反转矩阵的意义在于:我从起点s找到了其它点a,b,c……的最短路,那么如何找到从a到s,从b到s,从c到s……的最短路呢?我跑一次dj算法的话,得到的是一个数组,存储了从s到其它点的最短路径,如果我反转矩阵再跑一次,得到的便是从a到s,从b到s,从c到s……这一系列的最短路径集合!然而如果不反转矩阵的话,我得对于a点跑一次dj算法,对于b点跑一次dj算法,对于c点跑一次dj算法……这和Floyd算法就没啥区别了,明明可以使用2次dj算法解决的问题,我使用了V(顶点数)次,自然花费的时间就长了
- bellman-ford算法应该循环n-1次就结束了,如果第v次仍然更新那么有负的环,而不是循环n次判断n+1次
- bidirectional双向的、unidirectional单向的、monodirectional单向的
图相关
- dj算法如果超时的话,要使用邻接表存储边
- int 类型的5000*5000数组占用内存大小约为100M
关于计数
如果统计的是某个元素出现的次数,如果这个元素可以枚举(比如字符出现的次数,长度为x的木棒出现的次数),可以使用“桶”进行存储,即:
将所有可能出现的元素用数组代表,数组脚标代表这个元素,数组内容代表这个元素出现的次数,如果没有出现就是0