算法小知識
結構體之間可以相互賦值
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()函數只適用於浮點型()
整型除以浮點型 得 浮點型
整形減去浮點型 也得 浮點型
唯一分解定理:一個數能且只能分解爲一組質數的乘積
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;
}
};