容易忽略的算法知識點

算法小知識

結構體之間可以相互賦值

n<<1 == n*2
n>>1 == n/2
1<<n == 2^n
a^b == (a+b)%2

a&1==1 說明a是奇數
b&1==0 說明b是偶數

絕對值:fabs()函數可適用於整型和浮點型
次冪:pow() 函數只適用於浮點型
開平方:sqrt() 函數只適用於浮點型
根下平方和:hypot()函數只適用於浮點型(a=b2+c2a=\sqrt {b^2+c^2}

整型除以浮點型 得 浮點型
整形減去浮點型 也得 浮點型

唯一分解定理:一個數能且只能分解爲一組質數的乘積

cin.peek 是從看緩衝區中的下一個字符,但不接收

reverse() 函數不僅可用於數組,還可用於 string 和 STL 容器

藍橋杯不能使用 to_string、stoi、stol、auto、unordered_map、unordered_set 這些函數

可以添加 std::ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); 用於加快 cin 和 cout 的運行速度
還可以添加 #pragma GCC optimize(2)#pragma GCC optimize(3) 用於卡常O2優化

在非遞歸的函數前加上 inline 可以提高運行速度

如果 g++ 編譯器不夠快,可以考慮 clang++;

按照NOIP評測機的標準,1 秒完成的複雜度運算:
O(n) 的話就是 108,保險起見 106
O(n2) 的就是 104,保險起見 103

(階乘)10000! 有35659位
(次冪)2130 有 39 位

(階乘之和)只有 第一位 和 第三位 不是以 3 結尾,其他都是以 3 結尾(沒有以 0 結尾的)
舉例:1 3 9 33 153 873 5913 46233 409113 4037913 43954713 522956313 6749977113

求位數的公式

int s = log10(n)+1;  //log10是頭文件自帶的

2n 的求位數公式

int s = n*log10(2)+1;

n!的末尾 0 的個數

int n,ans=0;
cin>>n; //階乘
while(n)
{
    ans+=n/5;
    n/=5;
}
cout<<ans; //個數

第 K 小的數(nth_element)

int a[] = {1,5,6,9,8,7,4,2,3};
int len = sizeof(a)/sizeof(a[0]);
nth_element(a,a+k,a+len);
cout<<a[k];

//注意存在第 0 小, 所以第 k 小相當於第 k+1 小

最大數 與 最小數(max_element 與 min_element)

int a[] = {1,5,6,9,8,7,4,2,3};
int len = sizeof(a)/sizeof(a[0]);
auto p = max_element(a,a+len);
cout<< p-a <<' '<< *p;
cout<< endl;
auto q = min_element(a,a+len);
cout<< q-a <<' '<< *q;

output:
3 9
0 1

printf("%-5d", a)

printf("%-5d",2); //代表向左靠齊
printf("%-5d",-1);

output:
2    -1

整型 最大值與最小值

INT_MAX 和 INT_MIN 在頭文件< limits.h >中,代表 int 型的最大值與最小值,用法:

#include <limits.h>
cout<<INT_MAX<<' '<<INT_MIN<<endl;

Output:
2147483647 -2147483648

變量可以直接加負數 或 分數

int sum = 0;
sum += -1;

double sum = 0;
sum += 1.0/3;

兩個函數:toupper() 和 tolower(c)

toupper(c): 能將字符c轉換爲大寫英文字母
tolower(c): 能將字符c轉換爲小寫英文字母

五個函數:islower() 和 isupper() 和 isalpha() 和 isalnum() 和 isdigit()

islower(c): 當參數c爲小寫英文字母(a-z)時,返回 非0 值,否則返回 0
isupper(c): 當參數c爲大寫英文字母(A-Z)時,返回 非0 值,否則返回 0
isalpha(c): 當參數c爲英文字母 (A-Z) 或 (a-z) 時,返回 非0 值,否則返回 0
isalnum(c):當字符變量c爲字母或數字,返回 0,否則返回 非0值

isdigit(c):當字符變量c爲數字,返回 0,否則返回 非0值

string 字符串之間可以比較大小

"1814/09/06 day" 小於 "2014/09/06 day"

四捨五入的方法

double sum = 3.7;
sum = (int)(sum + 0.5);
int a = 1, b = 2, sum;
sum = (int)((a + b) * 1.0 / 2.0)
//另一種程序自動四捨五入的方法
double a=0.004;
double b=0.005;
printf("%.2lf\n",a);
printf("%.2lf",b);

//從結果來看,對於格式化,程序會自動四捨五入
output:0.00
	   0.01

cin 的妙用

如果你定義了一個 int 型,那麼當 cin 輸入表達式的值是就會檢測輸入的是不是數字,如果程序發現用戶輸入了錯誤內容時,會返回 0,否則返回 1

int a;
if(cin>>a) cout<<"輸入格式符合"<<endl;
cin.clear();
if(!(cin>>a)) cout<<"輸入格式不符";

output:
1
輸入格式符合
s
輸入格式不符

scanf 的妙用

在你輸入字母時,因爲scanf("%d",&num)中格式要求是整型(%d),所以不符合,返回值爲 0,而當你輸入任何數字時,scanf的返回值都是 1

int num,tmp;
int a = scanf("%d",&num);
int b = scanf("%d %d",&num, &tmp);
int c = scanf("%d",&num);
cout << a <<' '<< b <<' '<< c;

input:
1
1 2
a

output:
1 2 0

接收一行字符串

string s;
getchar(); //正常情況要加上
for (int i = 0; i < n; i++)
	getline(cin,s); //接收多行數據

// 空格在 getline中也算一個字符,上面適用於 string

char str[20];
while(gets(str)!=NULL)
	printf("%s",str);

// 空格在 gets中也算一個字符,上面適用於 char[]

典型的字符串的字符輸入

while((c=getchar())!='\n') 
{
	...
}

輸出固定位小數點的數(可以混用)

// C++
cout<<fixed<<setprecision(7)<<a<<endl

// C
printf("%.7lf",a)

輸出固定位數字的數

//C
printf("%05d",a); // a = 3

output: 00003

不同類型的位數

在這裏插入圖片描述

填充數組(memset 和 fill )

int m=5,n=5,value=1;
int a[n], G[m][n];
vector<int> v(5); //5個元素,自動初始化爲 0

//對於一維數組
memset(a,n,value);
fill(a,a+n,value);

//對於二維數組
fill(G[0],G[0]+m*n,value);

//對於容器
fill(v.begin(),v.end(),value);

申請棧空間

int *data = new int[100005];

delete[] data;

求已排列數組兩兩之間差的最大公約數(一步到位)

//兩兩相減取最大公約數 
for(int j = 0;j<(n-2);j++)
	temp = max(temp,gcd(a[j+1]-a[j],a[j+2]-a[j+1]));

關於運算符

運算符 描述
& 按位與運算符: 參與運算的兩個值,如果兩個相應位都爲1,則該位的結果爲1,否則爲0
| 按位或運算符: 只要對應的二個二進位有一個爲1時,結果位就爲1。
^ 按位異或運算符: 當兩對應的二進位相異時,結果爲1
~ 按位取反運算符: 對數據的每個二進制位取反,即把1變爲0,把0變爲1 ;~x 類似於 -x-1
<< 左移動運算符: 運算數的各二進位全部左移若干位,由 << 右邊的數字指定了移動的位數,高位丟棄,低位補0。
>> 右移動運算符: 把">>"左邊的運算數的各二進位全部右移若干位,>> 右邊的數字指定了移動的位數

下面以變量 a 爲 60,b 爲 13 舉例,二進制格式如下:

a = 0011 1100

b = 0000 1101

-----------------

a & b = 0000 1100

a | b = 0011 1101

a ^ b = 0011 0001

~a  = 1100 0011

a << 2 = 1111 0000

a >> 2 = 0000 1111

打印二進制

將二進制(8個字節)打印成 '*'' ' 的形式
for(int i = 7;i>= 0;i--)    //從字節最左邊開始打印
{
    if(x&(1<<i)) putchar('*');
    else putchar(' ');
}
此做法不僅包含了十進制轉換二進制,還包含了打印,因爲使用 & 時十進制會轉換成二進制

那負數怎麼辦?用補碼(即+256再轉換成二進制)
if(x< 0) x+= 256;//取補碼

結構體 與 STL 的聯繫

struct Node
{
	string s;
	int n;
	char c;
	Node(string ss,int nn,char cc):s(ss),n(nn),c(cc){}; //構造器 (函數)
};

queue<Node> q;
q.push(Node("123",456,'7')); //直接加入構造器

Node tmp = q.front();
string tmp_1 = tmp.s;
int tmp_2 = tmp.n;
char tmp_3 = tmp.c;

cout<<tmp_1<<tmp_2<<tmp_3;

output: 1234567

lower_bound/upper_bound

begin和end是 int 值,num是有6個元素的 int 型數組名

lower_bound( begin,end,num)查找第一個大於或等於num的數字,找到返回該數字的地址,不存在則返回end
upper_bound( begin,end,num)查找第一個大於num的數字,找到返回該數字的地址,不存在則返回end

int pos1 = lower_bound(num,num+6,7)-num;    //返回數組中第一個大於或等於 7 的下標或 6(沒找到)
int pos2 = upper_bound(num,num+6,7)-num;    //返回數組中第一個大於 7 的下標或 6(沒找到)

int pos3 = lower_bound(num,num+6,7,greater<int>())-num;  //返回數組中第一個小於或等於 7 的下標或 6(沒找到)
int pos4 = upper_bound(num,num+6,7,greater<int>())-num;  //返回數組中第一個小於 7 的下標或 6(沒找到)

記得一定要寫在最後減去num,不然程序運行不了
補充:若是 STL 則最後可以減去 temp.begin()

補個例子

int a[10] = {0,1,2,3,4,5};
int b1 = upper_bound(a+1,a+1+5,2)-(a+1); //找得到
int b2 = upper_bound(a+1,a+1+5,6)-(a+1); //找不到
cout << b1 <<' '<< b2;

output: 2 5

等差素數列的最小公差(定理)

//如:7,37,67,97,127,157 
//這樣完全由素數組成的等差數列,叫等差素數數列,上邊的數列公差爲 30,長度爲 6
//定理:等差素數列 的 最小公差 即爲數列長度以內的素數積

30 = 2 * 3 * 5
210 = 2 * 3 * 5 * 7
……以此類推

迴文數的判斷

bool pd_h(int x)
{

    int y=x,num=0;
    while (y!=0)
    {
        num=num*10+y%10;
        y/=10;
    } 
    if (num==x) return 1;
    else return 0;
}

排列組合

A(4,2)=4!/2!=4*3=12

C(4,2)=4!/(2! * 2!)=4 * 3/(2 * 1)=6

【補充】(A(n,0)=0 ,C(n,0)=0 )

僞隨機函數: rand()函數

如果想要表示一個數是從0開始到最大值的,比如說,想要產生一個0-99之間的隨機數,那麼用法如下

int num = rand() % 100

如果想要產生一個數是從1開始到最大值的,比如說,想要產生一個1-100之間的隨機數,那麼用法如下

int num = rand() % 100 + 1;

需要注意最後 +1和不 +1的區別,+1的最小值是1,不+1的最小值是0

全排列函數 next_permutation 用法

//建立一個數組
int num[3]={1,2,3};

do{  
  cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;  
}while(next_permutation(num,num+3)); //全排列整個數組

output:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

do{  
  cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;  
}while(next_permutation(num,num+2)); //將 3 改爲 2,即只全排列前兩個元素

output:
1 2 3
2 1 3

//因爲全排列是按照升序排列的,所以若更改數組順序
int num[3]={2,1,3};

do{  
  cout<<num[0]<<" "<<num[1]<<" "<<num[2]<<endl;  
}while(next_permutation(num,num+3)); //依然全排列整個數組

output:
2 3 1
3 1 2
3 2 1

列表排序的經典問題(不用結構體)

1025 反轉鏈表

數組圖案的旋轉

N 爲正方形數組的行與列(從1開始計數)
原數組A[N+1][N+1],旋轉後的數組B[N+1][N+1],存在的關係如下:

順時針旋轉90°: B[j][N-i+1]=A[i][j];
逆時針旋轉90°: B[N-j+1][i]=A[i][j];
水平方向翻轉 : B[i][N−j+1]=A[i][j];
垂直方向翻轉 : B[N-i+1][j]=A[i][j];

判斷合法日期

int isDate(int year, int month, int day)
{
	if(year>2020 || year<1900) return 0;  //具體年份依題目而定
	if(month>12 || month<1) return 0;
	if(month==1||month==3||month==5||month==7||month==8||month==10||month==12){
		return (day>=1&&day<=31);
	}
	if(month==4||month==6||month==9||month==11) {
		return (day>=1&&day<=30);
	}
	if((year%4==0&&year%100!=0)||(year%400==0)){
		return (day>=1&&day<=29);
	}
	return day>=1&&day<=28;
}

求 n!的非零尾數

// 第一種方法(知道 n!有多少尾數零時,簡化了運算)
int n;cin>>n; //階乘 n 
int t=n,ans=0;
while(t)
{
    ans+=t/5;
    t/=5;
}
int cnt2=ans,cnt5=ans,ans1=1;
for(int i=1;i<=n;i++) //求階乘 
{
    int t=i;
    while(cnt2&&t%2==0)t>>=1,cnt2--; // 減少因子 2 的個數 
    while(cnt5&&t%5==0)t/=5,cnt5--; // 減少因子 5 的個數 
    ans1=ans1*t%10; //結果取模 
}
cout<<ans1;


// 第二種方法
int n;cin>>n; //階乘 n 
int ans=1;
for(int i=1;i<=n;i++)
{
    ans=ans*i%1000000; //完成階乘運算且控制了數值大小
    while(ans%10==0)
    {
        ans=ans/10;
    }
}
cout<<ans;

超長整型 __int128(可容納 39 位十進制數字)與 快讀、快輸

最長的 unsigned long long 只能容納 19 位十進制數字,所以衍生了自創的超長整型
超長整型 __int128 也只能由 快輸 來輸出

#include <bits/stdc++.h>
using namespace std;
inline __int128 read() //兩條下劃線  //39位  //unsigned long long 19--20位 //快讀
{
    __int128 x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

inline void write(__int128 x) //快輸
{
    if(x<0){putchar('-');x=-x;}
    if(x>9){write(x/10);}
    putchar(x%10+'0');
}

int main()
{
    __int128 a = read();   //新類型
    __int128 b = read();
    int c = 4;   //傳統類型
    write(a);write(b);write(c);putchar('\n');  //直接打印
    write(a + b);write(a - b);write(a * b);write(a / b);putchar('\n');  //新類型之間 加減乘除
    write(a + c);write(a - c);write(a * c);write(a / c);putchar('\n');  //新類型 與 傳統類型 加減乘除
    return 0;
}

input:
8 4
output:
8 4 4        //實際打印後中間沒有空格,爲了方便觀看而手動加的
12 4 32 2
12 4 32 2

補充:正常版(int)的 快讀 快輸

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

手寫隊列(queue)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000 + 100;
struct queue
{
    int l = 0,r = 0,a[maxn];
    void push(int x){
        a[r++] = x;
    }
    int front(){
        return a[l];
    }
    void pop(){
        l++;
    }
    int empty(){
        return l >= r ? 1 : 0;
    }
}q;
int main()
{
	q.push(123);
	cout<<q.front();
	q.pop();
	if(q.empty()) cout<<' '<<456;
}

output: 123456

在這裏插入圖片描述

函數重載 與 構造函數

//簡單的初始化和重載的初始化
struct node{                   		    //定義一個點的座標結構體
    int x,y;                            
    node():x(0),y(0){}                  //默認的構造函數,初始化爲x=0,y=0 
    node(int _x,int _y):x(_x),y(_y){}   //重載的構造函數,使用兩個參數初始化x,y 
}; 

//使用示例
node a;                                 //默認構造函數,自動初始爲(0,0)
node b=node(3,4);                       //使用重載的構造函數,初始化爲(3,4)

//複雜類型的初始化,在默認構造函數中實現初始化
struct st{                     		    //學生結構體
    string id;                          //學生id
    int score[10];                      //記錄10門課程成績的數組,-1表示缺考
    bool pass;                          //是否合格的標記
    st(){
        fill(score,score+10,-1);        //初始化爲-1,表示缺考
		pass=true;                      //默認合格,不合格時修改標記
    }
};                                      //默認構造函數,定義時便完成初始化

//結構體比較,對 < 的重載,與 sort 聯合使用,不用加 cmp 直接排序的作用
struct node{
    int x,y;
    bool operator < (const node &b)const//重載小於號,用於構建優先級關係
    {
        if(x!=b.x)    return x<b.x;
        else          return y<b.y;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章