【学习经验】从学长学姐继承下来的板子们
二分查找/二分法
查找a中是否存在key
1
2
3
4
5
6
7
8
9
10
11
12
13int find(int a[], int n, int key){
int l = 0, r = n - 1, mid;
while (l <= r){
mid = l + (r - l) / 2; //左右边界中间点的值
if (a[mid] < key) l = mid + 1;
//如果当前的mid比目标小,则左边界右移到mid右边
else if (a[mid] > key) r = mid - 1;
//如果当前的mid比目标大,则右边界左移到mid左边
else return mid;
//如果当前的mid等于目标,返回mid
}
return -1; //没找到key
}查a中第一个大于等于 key 的
1
2
3
4
5
6
7
8
9
10
11
12int find_first(int a[], int n, int key){
int l = 0, r = n - 1, mid;
while (l <= r){
mid = l + (r - l) / 2;
if (a[mid] < key) l = mid + 1;
//如果当前的mid<目标,则左边界右移到mid右边
else r = mid - 1;
//如果当前的mid>=目标,则右边界左移到mid左边
}
if (l < 0 || l >= n) return -1; //key<a[0] || key>a[n-1]
return a[l] == key ? l : -1; //如果key不存在,返回-1
}查a中第一个大于key的
1
2
3
4
5
6
7
8
9
10
11
12int find_last(int a[], int n, int key){
int l = 0, r = n - 1, mid;
while (l <= r){
mid = l + (r - l) / 2;
if (a[mid] <= key) l = mid + 1;
//如果当前的mid<=目标,则左边界右移到mid右边
else r = mid - 1;
//如果当前的mid>目标,则右边界左移到mid左边
}
if (l < 0 || l >= n) return -1; //key<a[0] || key>a[n-1]
return a[l] == key ? l : -1; //如果key不存在,返回-1
}二分法求单调(递增)函数func(x)的解
1
2
3
4
5
6
7
8
9
10double solve(double left, double right, double y){
double mid;
while (right - left > eps){
mid = (right + left) / 2;
if (func(mid) > y) // 递减则 func(mid) < y
right = mid;
else left = mid;
}
return mid;
}
排序
冒泡排序-升序
1
2
3
4
5
6
7
8
9
10
11void bubble_sort(int a[], int n){
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++){
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}选择排序-升序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void select_sort(int a[], int n) {
for (int i = 0; i < n - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < n; j++) {
if (a[j] < a[min_idx]) {
min_idx = j;
}
}
if (min_idx != i) {
int temp = a[i];
a[i] = a[min_idx];
a[min_idx] = temp;
}
}
}qsort快排
1
2
3
4
5
6
7
8
9
10qsort(a, n, sizeof(double), cmp);
//a 需要排序的数组名称 n 需要排序的个数 sizeof(double) 一个元素的大小
//升序排序,最基础的写法
int cmp(const void*p1, const void*p2){
double *d1=(double *)p1; //转换为目标类型
double *d2=(double *)p2; //转换为目标类型
if((*d1 - *d2) < -eps) return -1; //返回负数=p1在p2前面
else if((*d1 - *d2) > eps) return 1; //返回正数=p1在p2后面
else return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 结构体的指针数组的cmp
int cmp(const void *p1, const void *p2){
struct tnode **d1 = (struct tnode **)p1;
struct tnode **d2 = (struct tnode **)p2;
if((*d1)->weight < (*d2)->weight){
return -1;
}else if((*d1)->weight > (*d2)->weight){
return 1;
}else{
if((*d1)->c < (*d2)->c){
return -1;
}else{
return 1;
}
}
}归并排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39void msort(int left, int right){ //归并排序
if(left == right){ // 只剩一个元素,已经有序,无需排序
return;
}
int mid = (left+right)/2;
// 排序左侧
msort(left, mid);
// 排序右侧
msort(mid+1, right);
// 合并
int left_i = left, right_i = mid+1, i=left;
while(left_i<=mid && right_i<=right){
if(a[right_i] < a[left_i]){
temp[i++] = a[right_i];
right_i++;
}else if(a[right_i] > a[left_i]){
temp[i++] = a[left_i];
left_i++;
}else{
temp[i++] = a[right_i];
temp[i++] = a[left_i];
left_i++;
right_i++;
}
}
while(left_i<=mid){
temp[i++] = a[left_i++];
}
while(right_i<=right){
temp[i++] = a[right_i++];
}
for(int j=left; j<=right; j++){
// printf("%d ", temp[j]);
a[j] = temp[j];
}
// puts("");
}
高精度
高精度加法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include <stdio.h>
#include <string.h>
#define MAX 1000005
char s1[MAX], s2[MAX];
int a1[MAX], a2[MAX];
int main() {
int len, len1, len2, i, j;
scanf("%s", s1); //s1读入第一个数字
scanf("%s", s2); //s2读入第二个数字
len1 = strlen(s1);
len2 = strlen(s2);
for (i = 0; i < len1; i++)
//字符串倒序导入数组,方便从低位开始运算
a1[i] = s1[len1 - 1 - i] - '0';
for (i = 0; i < len2; i++)
//字符串中的是字符,要转换成数字
a2[i] = s2[len2 - 1 - i] - '0';
len = (len1 > len2) ? len1 : len2;
for (i = 0; i < len; i++) {
a1[i + 1] += (j = (a1[i] + a2[i]) / 10);//进位
a1[i] = (a1[i] + a2[i]) % 10;//保留
}
if (j) len++;//最后一位是否进位
for (i = 0; i < len; i++)
printf("%d", a1[len - 1 - i]);
return 0;
}高精度减法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 1000005
char s1[MAX], s2[MAX];
int a[MAX], b[MAX];
int main() {
int len, i;
scanf("%s %s", s1, s2);
int l1 = strlen(s1);
int l2 = strlen(s2);
int flag = 0;
if (l1 < l2 || (strcmp(s1, s2) < 0 && l1 == l2)) { //s2代表的数更大
len = l2;
flag = 1;
for (i = l2 - 1; i >= 0; i--)
a[l2 - i - 1] = s2[i] - '0';
for (i = l1 - 1; i >= 0; i--)
b[l1 - i - 1] = s1[i] - '0';
} else {
len = l1;
for (i = l1 - 1; i >= 0; i--)
a[l1 - i - 1] = s1[i] - '0';
for (i = l2 - 1; i >= 0; i--)
b[l2 - i - 1] = s2[i] - '0';
}
for (i = 0; i < len; i++) {
a[i] -= b[i];
if (a[i] < 0) {
a[i + 1] -= 1;
a[i] += 10;
}
}
while (a[len - 1] == 0 && len > 1) len--;
if (flag == 1) printf("-");
for (i = len - 1; i >= 0; i--) printf("%d", a[i]);
printf("\n");
return 0;
}高精度乘法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#include <stdio.h>
#include <string.h>
#define MAX 1000005
char s1[MAX], s2[MAX];
int a1[MAX], a2[MAX], ans[MAX];
int main() {
int i, j, len1, len2;
scanf("%s%s", s1, s2);
len1 = strlen(s1);
len2 = strlen(s2);
for (i = 0; i < len1; i++) {
a1[i] = s1[len1 - 1 - i] - '0';
}
for (i = 0; i < len2; i++) {
a2[i] = s2[len2 - 1 - i] - '0';
}
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
ans[i + j] += a1[i] * a2[j];
}
}
for (i = 0, j = 0; i < len1 + len2; i++) {
ans[i] += j;
j = ans[i] / 10;
ans[i] %= 10;
}
for (; ans[i] == 0; i--){
if(i<0){
printf("0");
break;
}
} //跳过前面的0
for (; i >= 0; i--) {
printf("%d", ans[i]);
}
return 0;
}
最大公约数
1 |
|
输出全排列
1 |
|
计算组合数
1 |
|
快速幂取模-a的b次方%p
1 |
|
假设我们拿到了a,并且b=11。想求 a11,但是又不想乘11次,有点慢。以电脑视角稍稍观察一下 b=(11)10,二进制下是 b=(1011)2。制作一个base。现在base = a,表示的是,a1 = a。待会base会变的。制作一个ans,初值 11,准备用来做答案。
1
2while(b > 0)
{循环一。看,b(二进制)的最后一位是11吗?是的。这代表a11=a8×a2×a1中的“ ×a1 ”存在。所以 ans*=base。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17if(b & 1)
ans *= base;
/*关于 b & 1:
“&”美名曰“按位与”。
x & y 是二进制 x 和 y 的每一位分别进行“与运算”的结果。
与运算,即两者都为 1 时才会返回 1,否则返回 0。
那么 b & 1
二进制
b = 1011
1 = 0001
b&1 = 0001
因为 1(二进制)的前面几位全部都是 0,
所以只有 b 二进制最后一位是 1 时,b & 1 才会返回 1。
挺巧妙的,并且很快。)*/然后base努力上升,他通过自乘一次,使自己变成a2。
1
base *= base;
同时
1
b >>= 1;
它把(二进制的)自己每一位都往右移动了。原来的最后第二位,变成了最后第一位。b=(101)2。
1
}
循环二,再看看b,最后一位还是11。这说明有“ ×a2 ”,ans∗=base。base继续努力,通过base∗=base让自己变成了a4。然后b也右移一位。b=(10)2。
循环三,可是b的最后一位不再是1了,说明不存在“×a4”。base自我升华,达到了a8。且 b>>=1。这一步中,答案没有增加,可是毕竟 b>0,还有希望。
循环四,b的最后一位是1,这说明“×a8”的存在。ans∗=base。由于b再右移一位就是0了,循环结束。
总的来说,如果b在二进制上的某一位是1,我们就把答案乘上对应的a2^n。
一些相关笔记补充
直接求幂可以用循环解决,但是会出现溢出的问题
1
2
3
4
5
6
7long long normal(long long base,long long power){
long long result=1;
for(int i=1;i<=power;i++){
result*=base;
}
return result;
}取模运算的法则:
(a+b)%p=(a%p+b%p)%p (ab)%p=(a%pb%p)%p,利用这一法则可以在循环乘积每一步前先取模来防止溢出
快速幂算法:每一步将指数分成两半,相应底数做平方运算,最后求出的结果实际是指数为奇数时的底数相乘
1
2
3
4
5
6
7while(power>0){
if(power&1==1){
result=result *base;
}
power>>=1;
base=base*base;
}
汉诺塔
1 |
|
判断质数
法一:直接判断
1
2
3
4
5
6
7int is_prime(int x){
if (x < 2) return 0;
for (int i = 2; i * i <= x; i++){
if (x % i == 0) return 0;
}
return 1;
}生成质数表再查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//利用已有的质数表primes,判断n是否为质数
int isPrime(int primes[], int n){
int i;
for(i=0; primes[i]*primes[i] <= n; i++){
if(n % primes[i]== 0)
return 0;
}
return 1;
}
//构造≤Q的质数表(Q>=5)
void init_primes(int primes[], int Q){
int count=3, num, step;
primes[0] = 2; primes[1] = 3; primes[2] = 5;//头3个质数
num = 7; step = 2;//初始为2
while(count < Q){
step = 6-step;// 构造4-2-4-2-...序列,减少遍历
if(isPrime(primes, num))
primes[count++] = num;
num += step;// 下一个可能的质数
}
}
判断闰年
1 |
|
计算逆序数
1 |
|
斐波那契数列
斐波那契数列
1
2
3
4int fb(int n){
if(n<=3) return 1;
return fb(n-3)+fb(n-1);
}广义斐波那契数列
1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <stdio.h>
long long gfb(long long n,long long p,long long q,long long a,long long b,long long c );
int main(){
long long n,t,p,q,a,b,c;
scanf("%lld%lld%lld%lld%lld%lld",&n,&p,&q,&a,&b,&c);
t=gfb(n,p,q,a,b,c);
printf("%lld",t);
return 0;
}
long long gfb(long long n,long long p,long long q,long long a,long long b,long long c ){
if(1==n) return p;
if(2==n) return q;
if(n>=3) return (a*gfb(n-1,p,q,a,b,c)+b*gfb(n-2,p,q,a,b,c)+c);
}
大数乘法取模-(a*b)%mod
1 |
|
计算三角形周长面积
1 |
|
进制转化
十进制转二进制小数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <stdio.h>
#include <string.h>
int main()
{
double xiao,x;
int zheng,a[50],b[11],i=0,cnt=0,len=0,tmp;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
scanf("%lf",&x);
tmp=zheng=(int)x; //整数部分
if(!zheng){ //整数是0
printf("0");
}else{ //整数不是0
while(zheng){
a[len++]=zheng%2;
zheng /= 2;
}
}
for(i=len-1;i>=0;i--){ //输出整数部分
printf("%d",a[i]);
}
printf("."); //输出小数点
xiao=x-tmp; //小数部分
while(xiao){
tmp=(int)(xiao*2);
b[cnt++]=tmp;
xiao=2*xiao-tmp;
}
for(i=0;i<10;i++){ //输出小数部分
printf("%d",b[i]);
}
return 0;
}二进制转十进制
1
2
3
4
5
6
7
8
9
10
11
12
13int bin2Dec(int arr[]){
/*
* 将2进制转为10进制
* 设1个int的32位bit由低到高存在arr[0]...arr[31]
* 函数命名是binary to decimal的缩写
*/
int ret = 0;
for (int i = 0; i < 32; i++){
ret += arr[i] * (1 << i);
//相当于ret += arr[i] * (int)pow(2, i);
}
return ret; //ret是得到的十进制数
}任意进制转换为2-36进制的long long
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41#include <stdio.h>
#include <ctype.h>
typedef long long ll;
/**
* Convert string to long long int
*
* @param s source string
* @param base 2 <= base <= 36
* @return long long int value
*/
ll str2ll(char s[], int base) {
ll sum = 0;
int x, i;
for (i = 0; s[i]; i++) {
if (isdigit(s[i])) x = s[i] - '0';
else if (islower(s[i])) x = s[i] - 'a' + 10;
else if (isupper(s[i])) x = s[i] - 'A' + 10;
else {
printf("Unexpected Character!");
return -1;
}
if (x >= base) {
printf("Unexpected Character!");
return -1;
}
sum *= base;
sum += x;
}
return sum;
}
int main() {
char s[32];
int x;
scanf("%s%d", s, &x); //s需要转换的字符串 //x进制
printf("%lld", str2ll(s, x));
return 0;
}
首个出现三次的字母
1 |
|
子串逆置
1 |
|
字符串归零(by onlyar)
1 |
|
字符串查找(或许你想写个strstr吗)
1 |
|
多关键字排序-所以说这位不知名的学长你为什么要三目运算符套三目运算符啊(害怕)
1 |
|
你绝对不会用到的-二的幂次方-才不是因为看不懂不敢用呢
1 |
|
我觉得不会再考一遍吧-双阶乘-但还是存一下吧
- 版本1
1 |
|
- 版本2
1 |
|
只能伸长不能缩短的变长数组
1 |
|