- C/C++ 基础知识篇:点击打开链接
- ACM IO 模板(控制台版):
#include<bits/stdc++.h> #include<cmath> #define mem(a,b) memset(a,b,sizeof a) #define ssclr(ss) ss.clear(), ss.str("") #define INF 0x3f3f3f3f #define MOD 1000000007 using namespace std; typedef long long ll; int main() { // int T; scanf("%d",&T); // int n; // while(T-- && ~scanf("%d",&n)) // { int n; while(~scanf("%d",&n)) { } return 0; }
- ACM IO 模板(文件版):
#include<bits/stdc++.h> #include<cmath> #define mem(a,b) memset(a,b,sizeof a) #define ssclr(ss) ss.clear(), ss.str("") #define INF 0x3f3f3f3f #define MOD 1000000007 using namespace std; typedef long long ll; int main() { freopen("data.txt", "r", stdin); // 必须创建,文件保存在源文件同个目录下 freopen("data.txt", "w", stdout); // 无需创建自动生成 /* 中间按原样写代码,什么都不用修改 */ int n; while(~scanf("%d",&n)) { printf("%d\n",n); } fclose(stdin); fclose(stdout); return 0; }
- URDL(二维 / 三维):
// URDL:二维 const int di[4]={-1,0,1,0}; const int dj[4]={0,1,0,-1}; // UDRL i from 0 to 3(二维) int dir[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 }; // URDL:三维 const int X[6]={1,0,0,-1,0,0}; const int Y[6]={0,1,0,0,-1,0}; const int Z[6]={0,0,1,0,0,-1};
- 斐波那契数列与三角形的关系。点击打开链接
- 卡特兰数。点击打开链接
- 有时候,全局变量对 DFS 是禁忌,特别是每次回溯时需要变回起初的值的变量,如果用全局替代局部变量的话,一旦改变就回不去起初的“还原点”了。
- DFS 时,有时候可以用比较好看出的值代入看看是否正常,大概感觉对即可,不求甚解。
- 正整数分解成(可相同或不相同的数)使得乘积最大。点击打开链接
- 快速求解二进制 1 的个数。
ll numOf1(ll n) { ll cnt = 0; while(n){ cnt++; // 只要 n 不为 0,则其至少有一个 1 n = n & (n - 1); } return cnt; }
- 算法竞赛中,关闭iostream对象和cstdio流同步以提高输入输出的效率。
1、即调用ios::sync_with_studio(false); 特别注意:关闭后C++ IO与C IO不能混用,cin不能与scanf,sscanf, getchar, fgets等混用,cout不能与printf,puts等混用,否则IO会混乱。
2、在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。即调用std::cin.tie(0)。std::ios::sync_with_stdio(false); std::cin.tie(0);
- 多重循环直接跳出技巧:
for(int i=0,f=1;i<n&&f;i++) for(int j=0;j<n&&f;j++) if(1) f=0;
-
C++不检查数组越界情况?
答:看编译器是否对这方面进行调改过,如果越界也不报运行错误的话,就报WA,但有些OJ编译器可以检查出的话就报RE。 -
“printf、scanf” 和 其他表达式可以用 “,” 写成一句,但是 “break、continue、return” 必须独自一句。
-
一道题目多种解决方法时,比较下时间复杂度。比如:点击打开链接(m都一样的情况:以 n 为主体AC,但以 k 为主体TLE)
-
当人脑debug时,或者一些数据不确定时,选择数据量尽可能少的例子来确定代码,因为代码是通用的。
-
输出的时候,尽可能用“&”引用(参数复制速度比较慢,所以上引用)。
-
PAT 中,gets 函数用不了。
-
对于一些分类的题目,可以用优先级标记来区分,这样只要一次sort即可,否则要多次sort,这样时间消耗方面还是有差别的。
-
有时候建树时,没出现的结点就是 root:
for(int i=0;i<n;i++) if(!vis[i]){rt=i; break;} /* 9 7 8 - - - - - - 0 1 2 3 4 5 - - - - */
-
保留小数部分技巧:如果判断保留 n 位小数,则判断第 n+1 位数字与 “5” 做比较再进行处理。
-
完全二叉树判定:第一次出现空儿子后,接下来如果有出现真儿子,则 NO,否则 YES。
-
有些题目不要全都算出来,需要用到哪个就算哪个,这样可以节省没必要算的时间。
-
写在一个括号里的变量需要注意:如果 bfs 函数里有对 rs 操作则最终效果是不理想的,因为这里的 rs 在没运行 bfs 之前就已经旧值赋值上去了,即使中途改变了 rs,也是 rs 被更新,但是这里的 %d 输出仍然是之前未改变的 rs 值:
printf(bfs()?"YES %d\n":"NO %d\n",rs);
- 小技巧:
for(int i=0;i<n;i++) { scanf("%d",&a[i]); if(i) join(a[i-1],a[i]); }
- 自定义稳定排序:添加个唯一值标号(从小到大比较)。
- char* 效率高于 string。
- 小技巧:写入名字 并 计算名字前有多少空格。
int calSp(char name[]) { char c; int cnt=0,k=0; while((c=getchar())==' ') cnt++; do { name[k++]=c; }while((c=getchar())!='\n'); name[k]='\0'; return cnt; }
- 50% 占比,小技巧:
// 行数是列数的50%(四舍五入取整) int n; cin>>n; int col=n,row=n-n/2;
- 模仿 split 分割小技巧:
string s="abc 123 . ",ts; stringstream ss(s); int k=0; while(ss>>ts) { k++; cout<<ts<<endl; cout<<k<<endl; }
- map<string, int> 搭配 scanf:
map<string,int> mp; char s1[100]; scanf("%s",s1); mp[s1]=1; // 容易 TLE string s2; cin>>s2; mp[s2]=1;
- 翻译:
1、“a by b” // a * b
- 对于局部数组来讲:如果数组在定义声明顺便初始化的时候,只要前面有数据赋值,多少个都可以,至少有一个即可,则,其后的数组里的元素全部为0,所以一般 int a[100]={0}; 并不是把全部数组元素变为0,而是因为刚刚说的这个原理。如果改成 int a[100]={1,2,3}; 则前3个为1,2,3,但后面都为0。
- 首尾空格处理 + strncpy:
// 首尾空格处理 int l=0,r=k-1; while(s[l]==' ') l++; while(s[r]==' ') r--; strncpy(s,s+l,r-l+1); ts[r-l+1]=0;
- strtok 函数:
char str[] = "I am a stduent,you are teacher!"; const char spliter[] = " ,!"; char * pch; pch = strtok( str, spliter ); while( pch != NULL ) { cout << pch << endl; pch = strtok( NULL, spliter ); }
- lower_bound / upper_bound 函数:
/* 数组从 1 开始存储,但是查找的时候依然是从 0 开始,因为若当待查找的数据>最大值,return 数组大小;若<最小值,return 0;而如果不从 1 开始存储的话,当 return 0 时,不知道是查找到第一个数据还是因为没找到的缘故。 */ int l=lower_bound(a,a+n+1,b)-a; // 在 a[i] 中,返回第一个大于等于 b 的数字的对应位置 int r=upper_bound(a,a+n+1,b)-a; // 在 a[i] 中,返回第一个大于 b 的数字的对应位置 int rs=r-l; // 表示有 rs 个等于 b 的数
- 四舍五入到整数小技巧:
printf("%d",(int)(x+0.5)); // 比下面这种写法有时更加精确 printf("%.0f",double(x));
- 上下界针对 y轴 来说,所以上界显然大于等于下界,也就是说:区间[下界,上界]。
- 进制互转:
/* 10进制转任意进制 */ void D2Any(int x,int r) { int a,b=x,k=0,ans[100]; while(1) { a=b%r; b=b/r; ans[k++]=a; if(x/r==0) break; x/=r; } for(k--;k>=0;k--) { if(ans[k]>9) printf("%c",ans[k]+87); // 87:小写,55:大写 else printf("%d",ans[k]); } } /* 任意进制转10进制 */ ll Any2D(char x[],int r) { int len=strlen(x),tmp=1; ll ans=0; for(int i=len-1;i>=0;i--) { if(x[i]>='0' && x[i]<='9') ans+=(x[i]-'0')*tmp; else ans+=(x[i]-'a'+10)*tmp; tmp*=r; } return ans; }
- 0.000005 处理精度丢失问题,具体要加多少个“0”,看题目的保留多少位的情况再做分析。
- 变量的 初始化 和 定义,尽量跟业务逻辑的代码紧挨在一起;先业务逻辑,需要用到的变量再往上面补充。
- 日期比较 y*10000 + m*100 + d 小技巧。
- 差分前缀和小技巧:点击打开链接
- 输入一个十进制,统计它的二进制数中“1”的个数:
int cnt1(int x) { int cnt=0; while(x) cnt++, x=x&(x-1); return cnt; }
- 输出格式控制:
// 第一种 4 line 每次都要判断相对来说也消耗 for(int i=0;i<n;i++) if(i) printf(" %d",a[i]); else printf("%d",a[i]); puts(""); // 第二种 4 line printf("%d",a[0]); for(int i=1;i<n;i++) printf(" %d",a[i]); puts(""); // 第三种 3 line // 最好不要从0开始,否则每次都要 -1 相对来说也要消耗(即使可忽略不计),所以从1开始比较好 for(int i=1;i<n;i++) printf("%d ",a[i]); printf("%d\n",a[n]); // 第四种 2 line for(int i=0;i<n;i++) printf("%d%c",i==n-1?'\n':' ');
- 输入格式控制:
char ch; scanf(" %c", &ch); // 百分号之前有一个空格,这样scanf会首先过滤掉所有的空格、制表符和换行符 /* 常见吸收回车写法 */ char ch; int n; scanf("%d",&n); getchar(); // 不推荐 scanf("%c",&ch); scanf("%d",&n); scanf(" %c",&ch); // 推荐
- 输入(一行有空格的字符串),希望不包括空格以 '\n' 结束:
/* 用 do-while 免得最后一次数据('\n'前)还要额外处理 */ do { scanf("%s",key); nds[i].kv.push_back(key); }while(getchar()!='\n');
- 利用格式符 “%[]” 作用:为扫描字符集合:
scanf("%[^c]", str);
其中 “c” 是一个具体的字符常量(包括控制字符)。
C语言中 scanf() 函数提供的 “%[]” 格式串可以用来进行多个字符的输入,并对结束符进行自定义。对于 %[] 还可以用 ^+ 任意字符(包括 eof)来结束字符串的输入,如 %[^EOF] 就是直到有 EOF 输入,字符串才中止。scanf("%[^\n]", str); // 以换行符作为字符串输入的结束(可包含空格)
- 针对非升序 / 非降序操作,如果相等按照原来输入的顺序进行排序(通过伪优先级控制):
struct node { int ge,pri; }nds[100005]; int cmp(node n1,node n2) // 非升序 { if(n1.ge==n2.ge) return n1.pri<n2.pri; return n1.ge>n2.ge; }
- 自定义进制(模仿 hh: mm: ss):
typedef long long ll; int main() { ll a1,b1,c1,a2,b2,c2; scanf("%lld.%lld.%lld%lld.%lld.%lld",&a1,&b1,&c1,&a2,&b2,&c2); ll rs=c1+b1*29+a1*17*29+c2+b2*29+a2*17*29; // rs(秒) hh=rs/3600 mm=rs%3600/60 ss=rs%60 printf("%lld.%lld.%lld\n",rs/(17*29),rs%(17*29)/29,rs%29); return 0; }
- 大数比较小技巧:点击打开链接
- double 判 0 小技巧:
double A; scanf("%lf",&A); if(A+0.005>=0&&A<0) printf("0.00"); // 1 if(fabs(A)<0.0005) printf("0.00"); // 2 推荐
- 矩阵区分八个方向是【越界】or【题目条件】的区分小技巧:
int pas=1, k=0; // 这里 pas 不能从 0 开始,否则会影响判断 if(i-1>=0 && pas++ && abs(a[i-1][j]-d)>st) k++; if(i-1>=0 && j+1<m && pas++ && abs(a[i-1][j+1]-d)>st) k++; if(i-1>=0 && j-1>=0 && pas++ && abs(a[i-1][j-1]-d)>st) k++; if(j-1>=0 && pas++ && abs(a[i][j-1]-d)>st) k++; if(j+1<m && pas++ && abs(a[i][j+1]-d)>st) k++; if(i+1<n && pas++ && abs(a[i+1][j]-d)>st) k++; if(i+1<n && j-1>=0 && pas++ && abs(a[i+1][j-1]-d)>st) k++; if(i+1<n && j+1<m && pas++ && abs(a[i+1][j+1]-d)>st) k++; if(k+1==pas) return 1;
- 有时候创建数据结构的情况比较复杂的时候,先不要急于创建,可以在思路Coding的过程中,发现需要用到哪个变量就创建哪个变量。
- 判断语句原则:先判断再决定;而不是先决定再判断。
- 数组初始化的三种常用方法:for循环浪费的时间最多,{0} 与 memset 耗时差不多。
- PAT的题目因为时间控制严格,而且它评测机制每次都一个测试用例一次,所以不到万不得已,不要像平常ACM一样去做清空操作。
- heap:
// 边插入边建堆 for(int i=1;i<=n;i++) { scanf("%d",&a[i]); make_heap(a+1,a+1+i,greater<int>()); // min heap } // 先建树再调整堆 make_heap(a,a+n); // max heap
- 区间合并小技巧:点击打开链接
- 在DFS中途就可以输出结果并且结束整个程序技巧
exit(0);
- '\0' 输出不是什么都没有,而是一个类似的空格,当它用 %c 单独输出时。
- 待更新...
ACM教程 - 基础篇
于 2018-05-28 18:30:56 首次发布