ACM比赛资料

算法

tools——第二个——中文——ok

工具——编译时加入以下命令——:-std=c++11

工具——编译选项——连接器——产生调试信息——yes

加速

#include <bits/stdc++.h>

using namespace std;

#define endl "\n"

#pragma GCC optimize(3, "Ofast", "inline")

#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)

int main()

{

	IOS;
	cout<<"hello c++!"; 

/*代码区*/

 return 0;

}

排序

快排

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;//跳出条件

    int i = l - 1, j = r + 1, x = q[l];
//下方是D——W因此需预留偏移量
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
//一次处理仅能将数组划分为满足以x为界限的排序,而我们要的是整个数组从小到大的排序因此需要递归处理
}

sort排序是优化后的快排,所以更快,但有些题目不仅仅要排序后的结果,因此掌握这种思想很有必要

归并排序

void merge_sort(int q[],int l,int r)
{
    if(l>=r) return ;
    int mid= l+r>>1;
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    int k=0,i=l,j=mid+1;
    while(i<=mid&&j<=r)
    {
        if(q[i]<=q[j])tmp[k++]=q[i++];
        else tmp[k++]=q[j++];
    }  
        while(i<=mid)tmp[k++]=q[i++];
        while(j<=r)tmp[k++]=q[j++];
        for(i=l,j=0;i<=r;i++,j++)
        {
            q[i]=tmp[j];
        }
}

前缀和

输入一个长度为 n 的整数序列。

接下来再输入 m个询问,每个询问输入一对 l,r

对于每个询问,输出原序列中从第 l 个数到第 r个数的和。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

接下来 m 行,每行包含两个整数 l和 r,表示一个询问的区间范围。

输出格式

共 m行,每行输出一个询问的结果。

数据范围

1≤l≤r≤n
1≤n,m≤100000
−1000≤数列中元素的值≤1000

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10

 题解

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],s[N];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    
    for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
    while(m--)
    {int l,r;
        cin>>l>>r;
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

用于计算数组的子集区间长度和

for(int i=1;i<=n;i++)cin>>a[i];  
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
int l,r;
cin>>l>>r;
result=s[r]-s[l-1];

1.s[0]=0;

2.数组从1开始;

3.s[r]-s[l-1]表示从L到R的元素和

高精度

高精度加法

vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;//存放结果
    int t = 0;//进位
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;//进位与否
    }

    if (t) C.push_back(t);//判断最高位要不要进1
    return C;
}

 一道高精度加法的完整代码

#include<bits/stdc++.h>
using namespace std;

vector<int> add(vector<int> &A,vector<int> &B)
{
    vector<int> C;
    int t=0;
    for(int i=0;i<A.size()||i<B.size();i++)
    {
        if(i<A.size())t+=A[i];
        if(i<B.size())t+=B[i];
        C.push_back(t%10);
        t/=10;
    }
    if(t)C.push_back(1);
    return C;
}

int main()
{
    string a,b;
    cin>>a>>b;
    vector<int>A,B;
    for(int i=a.size()-1;i>=0;i--)
    {
        A.push_back(a[i]-'0');
    }
    for(int i=b.size()-1;i>=0;i--)
    {
        B.push_back(b[i]-'0');
    }
    vector<int> C=add(A,B);
    for(int i=C.size()-1;i>=0;i--)
    {
        cout<<C[i];
    }
    
    return 0;
}

 高精度减法

给定两个正整数(不含前导 00),计算它们的差,计算结果可能为负数。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的差。

数据范围

1≤整数长度≤1051≤整数长度≤105

输入样例:

32
11

输出样例:

21

#include <iostream>
#include <vector>

using namespace std;

bool cmp(vector<int> &A, vector<int> &B)
{
    if (A.size() != B.size()) return A.size() > B.size();

    for (int i = A.size() - 1; i >= 0; i -- )
        if (A[i] != B[i])
            return A[i] > B[i];

    return true;
}

vector<int> sub(vector<int> &A, vector<int> &B)
{
    vector<int> C;
    for (int i = 0, t = 0; i < A.size(); i ++ )
    {
        t = A[i] - t;
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = 1;
        else t = 0;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    vector<int> C;

    if (cmp(A, B)) C = sub(A, B);
    else C = sub(B, A), cout << '-';

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl;

    return 0;
}

 高精度除法

给定两个非负整数(不含前导 00) A,B,请你计算 A/B 的商和余数。

输入格式

共两行,第一行包含整数 A,第二行包含整数 B。

输出格式

共两行,第一行输出所求的商,第二行输出所求余数。

数据范围

1≤A的长度≤100000
1≤B≤10000
B一定不为 0

输入样例:

7
2

输出样例:

3
1

 题解:


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> div(vector<int> &A, int b, int &r)
{
    vector<int> C;
    r = 0;
    for (int i = A.size() - 1; i >= 0; i -- )
    {
        r = r * 10 + A[i];
        C.push_back(r / b);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a;
    vector<int> A;

    int B;
    cin >> a >> B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    int r;
    auto C = div(A, B, r);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];

    cout << endl << r << endl;

    return 0;
}

高精*低精

#include <iostream>
#include <vector>

using namespace std;


vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}


int main()
{
    string a;
    int b;

    cin >> a >> b;

    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    auto C = mul(A, b);

    for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);

    return 0;
}

高精*高精

#include <iostream>
using namespace std;
int a[100010] , b[100010], c[2000010];
string x, y;
int main(){
    cin >> x >> y;
    int a1 = x.size();
    int b1 = y.size();
    int c1 = a1 + b1;
    for(int i = 0; i < a1; i++){
        a[a1 -i -1] = x[i] - '0';//输入第一个数
    }
    for(int i = 0; i < b1; i++){
        b[b1 -i -1] = y[i] - '0';//输入第二个数
    }
     
    for(int i = 0; i < a1; i++){
        for(int j = 0; j < b1; j++){
            c[i + j] += a[i] * b[j];//按位相乘并累加
        }
    }
    int x = 0;
    for(int i = 0; i < c1; i++){//处理进位
        c[i] += x;
        x = c[i]/10;
        c[i] %= 10;
    }
    int k = c1;//两数相乘后的位数不会超过两数位数相加之和,所以c1足够了
    while(c[k]==0 && k > 0){//处理前缀0
        k--;
    }
    for(int i = k; i >= 0; i--){//倒序输出
        cout << c[i];
    }
    return 0;
}

二分

整数二分

给定一个按照升序排列的长度为 n 的整数数组,以及 q个查询。

对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。

如果数组中不存在该元素,则返回 -1 -1

输入格式

第一行包含整数 n和 q,表示数组长度和询问个数。

第二行包含 n 个整数(均在 1∼100001∼10000 范围内),表示完整数组。

接下来 q 行,每行包含一个整数 k,表示一个询问元素。

输出格式

共 q行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回 -1 -1

数据范围

1≤n≤100000
1≤q≤10000
1≤k≤10000

输入样例:

6 3
1 2 2 3 3 4
3
4
5

输出样例:

3 4
5 5
-1 -1

 题解

#include<bits/stdc++.h>
const int N=100010;
int q[N];
int n,m;
using namespace std;
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        cin>>q[i];
    }
    
    while(m--)
    {
        int k;
        cin>>k;
        int l=0,r=n-1;
        while(l<r)
        {
            int mid =l+r>>1;
            if(q[mid]>=k)r=mid;
            else l=mid+1;
        }
        if(q[l]!=k)cout<<"-1 -1"<<endl;
        else
        {
            cout<<l<<' ';
            int l=0,r=n-1;
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(q[mid]<=k)l=mid;
                else r=mid-1;
            }cout<<r<<endl;
        }
            
            
            
    }
    
    
    
    return 0;
}

二分的关键是区间更新问题的处理,其本质是边界的寻找,

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}


mid=l+r+1>>1;的原因是避免死循环;

check(mid)一律写成q[mid]>=x(目标即“边界”)或q[mid]<=x

带上=是为了贴合模板,避免出现奇怪的问题因此统一一下。

如果数组内无目标值即为

q[l]!=x

l和r完全等价,x为目标值

浮点数二分

因为浮点型可以严格的除于2,所以浮点型二分不存在边界问题;


    double a;
    cin>>a;
    double l=-10000,r=10000;
    //while(fabs(r-l)>1e-8)
    for(int i=0;i<100;i++)
    {
        double mid=(l+r)/2;
        if(mid*mid*mid>=a)r=mid;
        else l=mid;
    }
        cout << fixed << setprecision(6) << l;

l,r的值是数的边界由题中得,如上面的代码解决的问题数的范围是-10000到10000


    double l=-10000,r=10000;
    //while((r-l)>1e-8)

 这两个本质是一样的,即精度达到一定程度;

经验:一般保留n位小数时l-r>1e-(n+2);

快速幂(欧拉降幂)

求a的b次方mod q;

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int qmi(int a,int k,int p)
{
    int res=1;
    while(k)
    {
        if(k&1)res=(ll)res*a%p;
        k>>=1;
        a=(ll)a*a%p;
    }
    return res;
}
int main(){
    int n;
    cin>>n;
    while(n--)
    {
        int a,b,p;
        cin>>a>>b>>p;
        cout<<qmi(a,b,p)<<"\n";
        
    }
    
    
    
    
    return 0;
}

用到数论知识,存在大量边界问题,建议直接背模板;

/*typedef long long ll;
int qmi(int a, int k, int p)
{
    int res = 1 ;
    while (k)
    {
        if (k&1) res = (ll)res * a % p;
        k >>= 1;
        a=(ll)a*a%p;
    }
    return res;
}*/ 
int qmi(int m, int k, int p)
{
    int res = 1 % p, t = m;
    while (k)
    {
        if (k&1) res = (long long)res * t % p;
        t = (long long)t * t % p;
        k >>= 1;
    }
    return res;
}

本模板中含有位运算相关知识

差分

输入一个长度为 n 的整数序列。

接下来输入 m个操作,每个操作包含三个整数 l,r,c表示将序列中 [l,r]之间的每个数加上 c。

请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数序列。

接下来 m 行,每行包含三个整数 l,r,c表示一个操作。

输出格式

共一行,包含 n 个整数,表示最终序列。

数据范围

1≤n,m≤100000
1≤l≤r≤n
−1000≤c≤1000
−1000≤整数序列中元素的值≤1000−1000≤整数序列中元素的值≤1000

输入样例:

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

3 4 5 3 4 2

题解

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i]-a[i-1];
    }
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        b[l]+=c;
        b[r+1]-=c;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=b[i]+a[i-1];
        cout<<a[i]<<" ";
    }
    
    return 0;
}

类似于数学中的求导和积分,差分可以看成前缀和的逆运算。

差分数组:

首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];

然后我们构造一个数组b : b[1] ,b[2] , b[3],,,,,, b[i];

使得 a[i] = b[1] + b[2 ]+ b[3] +,,,,,, + b[i]

也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。

考虑如何构造差分b数组?

最为直接的方法

如下:

a[0 ]= 0;

b[1] = a[1] - a[0];

b[2] = a[2] - a[1];

b[3] =a [3] - a[2];

........

b[n] = a[n] - a[n-1]

for(int i=1;i<=n;i++)
{
    cin>>a[i];
    b[i]=a[i]-a[i-1]//a[]定义为全局变量,a[0]=0;
}
int l,r,c;
b[l]+=c;
b[r+1]-=c;
for(int i=1;i<=n;i++)
{
    a[i]=b[i]+a[i-1];
}

位运算 

在二进制下操作

1.右移运算符与&

n的k位是几(二进制下)

n>>k&1;

个位是第零位,十位是第一位;

应用:10进制下的2进制

eg:(10)10=(1010)2;

for(int i=31;i>=0;i--)
{
    cout<<(n>>k&1);

}

二进制是32位

2.lowbit

lowbit用于返回二进制下的最后一个1;

lowbit的内部实现

int lowbit(int x)
{
    return x&-x;

}

应用:统计二进制下1的个数

while (x)x-=lowbit,cnt++;

DP

背包问题

1. 0 1背包

「0-1 背包」是较为简单的动态规划问题,也是其余背包问题的基础。

动态规划是不断决策求最优解的过程,「0-1 背包」即是不断对第 i个物品的做出决策,「0-1」正好代表不选与选两种决定

特点:每个物品仅能使用一次或0次
重要变量&公式解释


f[i][j]:表示所有选法集合中,只从前i个物品中选,并且总体积≤j的选法的集合,它的值是这个集合中每一个选法的最大值.
状态转移方程
f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])

f[i-1][j]:不选第i个物品的集合中的最大值
f[i-1][j-v[i]]+w[i]:选第i个物品的集合,但是直接求不容易求所在集合的属性,这里迂回打击一下,先将第i个物品的体积减去,求剩下集合中选法的最大值.
问题
集合如何划分

一般原则:不重不漏,不重不一定都要满足(一般求个数时要满足)

如何将现有的集合划分为更小的子集,使得所有子集都可以计算出来.

题目介绍
有 N件物品和一个容量为 V的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N];
int v[N],w[N];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j<v[i])
            f[i][j]=f[i-1][j];
            else 
            f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
        }
    }
    cout<<f[n][m];
    
    return 0;
    
}

f[n][m]表示在体积不超过m的前提下从前n件物品中选的最优解,

其值的来源既是:f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])

关键在+w[i]上;

通过穷举遍历所有情况,符合要求时更新f[n][m]的值,其本质是一种暴力

线性筛质数

暴力

#include<bits/stdc++.h>
using namespace std;
bool isprime(int a);
int main() {
int b;
cin>>b;
if(isprime(b)==1)cout<<"yes";
 
 
 
}
bool isprime(int a){
if(a<=1)return 0;
	for(int i=2;i*i<=a;i++){
		if(a%i==0)return 0;	
	}
	return 1;
}

埃氏筛

#include<iostream>
using namespace std;
cnst int N=1e8+10;
int f[N];
int main(){
//	  f[i]=1代表i不是素数,f[i]=0代表i是素数 
f[0]=1;
f[1]=1;
// 0 1是素数
    int n;
    cin>>n;
//    埃氏筛 
    for(int i=2;i<=n;i++){
    	if(f[i]==1)continue;
    	for(int j=2;i*j<=n;j++){
    		f[i*j]=1;
		}
    }
//    打印 
    for(int i=0;i<=n;i++){
    	if(f[i]==0){
    		cout<<i<<' ';
		}
	}
}

欧拉筛

时间复杂度最低且输出第K大的数有独到的优势;



#include <bits/stdc++.h>

using namespace std;

const int N=1e8+10;
	int p[N];
	int f[N];
int main()

{
	int d=0;
	f[0]=1;
	f[1]=1;
	int n;
	cin>>n;
	for(int i=2;i<=n;i++){
		if(f[i]==0){//如果没被标记过,那么i是质数 
			p[d++]=i;
		}
		for(int j=0;j<d;j++){
			if(p[j]*i<=n){//标记以i为最大因数的数为不是素数(除了1和本身) 
				f[p[j]*i]=1;
			}else{
				break;
			}
			if(i%p[j]==0){//如果p[j]是i的因数,那么后面的数都不是以i为最大因数的 
				break;
			}
		}
	}

int q;
cin>>q;
while(q--)
{
	int t;
	cin>>t;
	cout<<p[t-1]<<endl;
}
    return 0;

}

 搜索

深度优先算法,即DFS又称暴搜

关键:回溯,剪枝;

全排列问题

给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式

共一行,包含一个整数 n

输出格式

按字典序输出所有排列方案,每个方案占一行。

数据范围

1≤n≤7

输入样例:

3

输出样例:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

 代码

#include <bits/stdc++.h>


using namespace std;

const int N = 10;
int n , path[N];
bool st[N]; // 状态数组

void dfs(int u ) // 第几个数字,一共几个数字
{
    if(u == n)// 递归到最后一个数字
    {
        for (int i = 0; i < n; i ++ ) cout << path[i] << ' '; // 输出保存的结果
        puts(" ");
    }

    for (int i = 1; i <= n; i ++ )
        if (!st[i]) // 没有被用过的数
        {
            path[u] = i ;
            st[i] = true; // i被用过
            dfs(u + 1);// 走到下一层
            st[i] = false;// 恢复现场(所谓回溯)
        }
}

int main()
{

    cin >> n;
    dfs(0);
    return 0;
 皇后问题

n−皇后问题是指将 n个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。

现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。

注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

1≤n≤9

输入样例:

4

输出样例:

.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

 题解

#include <iostream>

using namespace std;

const int N = 20;

int n;
char g[N][N];
bool col[N], dg[N ], udg[N ];

void dfs(int u)
{
    if (u == n)
    {
        for (int i = 0; i < n; i ++ ) 
        {
            for(int j=0;j<n;j++)
            {
                cout<<g[i][j];
            }cout<<endl;
        }cout<<endl;      
        return;
    }//等价变形
/*  if (u == n)
    {
        for (int i = 0; i < n; i ++ ) puts(g[i]);
        puts("");
        return;
    }*/

    for (int i = 0; i < n; i ++ )
        if (!col[i] && !dg[u + i] && !udg[n + u - i])
        {
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n + u - i] = true;
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n + u - i] = false;
            g[u][i] = '.';
        }//und[n+u-i]等价与und[n+i-u]加n是加入偏移量,表示第i条(反)对角线;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            g[i][j] = '.';

    dfs(0);

    return 0;
}

bfs

迷宫问题

给定一个 n×m的二维整数数组,用来表示一个迷宫,数组中只包含 0 或 1,其中 0表示可以走的路,1 表示不可通过的墙壁。

最初,有一个人位于左上角 (1,1)处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。

请问,该人从左上角移动至右下角 (n,m)处,至少需要移动多少次。

数据保证 (1,1)处和 (n,m) 处的数字为 0,且一定至少存在一条通路。

输入格式

第一行包含两个整数 n和 m。

接下来 n 行,每行包含 m个整数(0 或 1),表示完整的二维数组迷宫。

输出格式

输出一个整数,表示从左上角移动至右下角的最少移动次数。

数据范围

1≤n,m≤100

输入样例:

5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

输出样例:

8

 题解:

#include<bits/stdc++.h>
using namespace std;
const int N = 110;
typedef pair<int, int> PII;

int n, m;
int g[N][N], d[N][N];

int bfs()
{
    queue< pair<int, int> > q;

    q.push({0, 0});//起点

    memset(d, -1, sizeof(d));//c++函数,将d中元素全部初始化为“-1”

    d[0][0] = 0;//标记走过


    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};//对应移动的方向

    while (q.size())//队列不为空
    {
        PII t = q.front();//取队头元素,当前所在的位置

        q.pop();//出队
/*   特别注意,应用queue的性质,先进先出,用一层,存一层,清一层,队列空时即循环结束,bfs问题没有递归,只有循环,遍历完所有层级即结束*/

        for (int i = 0; i < 4; i++)
        {
            int x = t.first + dx[i], y = t.second + dy[i];//移动

            if (x >= 0 && x < n && y >= 0 && y < m/*在范围内*/ && g[x][y] == 0/*0表示路,可走*/ && d[x][y] == -1/*该点第一次走*/)
            {
                d[x][y] = d[t.first][t.second] + 1;
                /*这句话表示从起点走到(x, y)
                的步数等于从起点走到(t.first, t.second)的步数加一,
                因为我们是从(t.first, t.second)都到(x, y)的。*/
                q.push({x, y});//将新坐标入队,更新起点
            }
        }
    }

    return d[n - 1][m -1];//返回(0,0)到终点的最短距离;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> g[i][j];

    cout << bfs() << endl;

    return 0;
}


STL

 vector

(不定数组)

vector的定义

#include<bits/stdc++.h>
using namespace std;
int main(){
	vector<int> a(n);
	//定义长度为n的不定数组 
    vector<int> a(n,m);//n为长度,m为所有n个数的初始化的值
    //	vector<int> a[10]; 也可以
	return 0
} 

 vector的初始化及遍历

#include<bits/stdc++.h>
using namespace std;
int main(){
	
	
	vector<int> a (10,0);
	//定义长度为10且都初始化为 0; 

	
		//遍历方式 
	for(auto x:a) cout<< x<<endl;
	//一种遍历方式 ,范围遍历c++11特有
	for(int i=0;i<a.size();i++){
		cout<<a[i];
	} //传统遍历 
	for(auto i=a.begin();i!=a.end();i++)
	{
		cout<<*i;
	}//运用迭代器auto既是vector<int>::iterator; 
	
	return 0;
} 

支持的函数

	//支持的函数
	a.size();//返回vector长度 
	a.empty() //a空返回1;else 返回0; 
	clear()//清空 
	front()/back()//返回vector的第一/最后一个的数
	push_back()//尾插
	pop_back()//尾删 
	begin()/end()//迭代器 

黑科技

支持两个vector比较按字典序(444>33333)

#include<bits/stdc++.h>
using namespace std;
int main(){
	
	
	vector<int> a (3,4);
	vector<int> b (4,3);
	if(a>b)cout<<1;
	else cout<<0;
 //输出为1 
	return 0;
} 

pair

 存储二元组 

pair的定义

pair<int ,int>p;//前后两个数据类型可任意 

pair的访问

p.frist;
p.second;

pair的初始化

	p=mack_pair(10,111) ;
	p={20,123};//c++11特有 

pair存储3个元素

pair<int, pair<int ,int>>p;

主要应用

按照某一特质排序,具体用法,将排序的特质放fist 

pair也可用于比较,按字典序

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main(){
    vector<pair<int, string> > vec;
    vec.push_back(make_pair(11111,"li" ));
    vec.push_back(make_pair(222, "liu"));
    vec.push_back(make_pair(33, "wang"));
    vec.push_back(make_pair(5, "zhao"));

    sort(vec.begin(), vec.end());

    for(auto num: vec){
        cout<<num.second << endl;
//输出为zhao   wang  liu  li(字典序)  
    }
}

string

#include<bits/stdc++.h>
using namespace std;
int main(){
	//string的一些操作 
	string s="abc";
	s+="rrr";
	s+='a';
	//cout<<"abcrrra"
	cout<<s.substr(1,3);
	//substr(n,m)n是起始下标(从0开始),m是个数 ,当m大于剩余总长
	//输出到结束为止,也可以不输入m表示从n开始到最后 
	
	//cout<<"bcr" 
	
	printf("%s\n",s.c_str()) ;
	//输出整个s 
	return 0;
}

queue

队列:先进先出,一头进,另一头出

支持函数

  size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素

清空队列

#include<bits/stdc++.h>
using namespace std;
int main(){
	  queue<int>q;
	  q=queue<int>();//清空队列 
	 
	return 0;
}

priority_queue

优先队列(堆)默认是大根堆:最大堆,最大二叉树数,根是最大值

支持函数

size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

定义成小根堆的方式

    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

或者

	 priority_queue<int>heap;
	 heap.push(-x) ;
	 //插入时插相反值 

stack

支持函数

   size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素

deque

加强版的vector,双端队列,功能牛波一,但慢的令人发指,用的不多

    size()
    empty()
    clear()
    front()/back()
    push_front()/pop_front()//队首插,队首删
    begin()/end()
    front()/back()//返回vector的第一/最后一个的数
	push_back()//尾插
	pop_back()//尾删 
    []//随机选取
set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)

set

set的应用

输入一段只含小写字母的字符串,如果一个人的用户名中不同字符的个数是奇数,那么他就是男性,否则她就是女性。给你一个表示用户名的字符串,请帮助我们的主人公用他的方法确定这个用户的性别。

#include<bits/stdc++.h>
using namespace std;
int main(){
    set<char>s; 
	char c;
    while(cin>>c) s.insert(c);
    int a=s.size();
    if(a%2==0)cout<<"CHAT WITH HER!";
    else   cout<<"IGNORE HIM!";
}

    set<int >s;//不能有重复元素 
    multiset<int>ms; //可以有 
支持函数

  insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器

map

支持函数

 insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()

 应用的优势

#include<bits/stdc++.h>
using namespace std;
int main(){
	map<string,int>a;
	a["zy"]=1;
	cout<<a["zy"];//当成数组来用 
//cout<<1;
	return 0;
}

unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)//优点
    不支持 lower_bound()/upper_bound(), 迭代器的++,--//缺点,无序
其他与之对应的基本一致

bitset

主要优点可以节省八倍空间

bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=//支持各种(位运算)运算
    []

    count()  返回有多少个1

    any()  判断是否至少有一个1
    none()  判断是否全为0

    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~//取反
    flip(k) 把第k位取反

精简的总结

vector, 变长数组,倍增的思想
    size()  返回元素个数
    empty()  返回是否为空
    clear()  清空
    front()/back()
    push_back()/pop_back()
    begin()/end()
    []
    支持比较运算,按字典序

pair<int, int>
    first, 第一个元素
    second, 第二个元素
    支持比较运算,以first为第一关键字,以second为第二关键字(字典序)

string,字符串
    size()/length()  返回字符串长度
    empty()
    clear()
    substr(起始下标,(子串长度))  返回子串
    c_str()  返回字符串所在字符数组的起始地址

queue, 队列
    size()
    empty()
    push()  向队尾插入一个元素
    front()  返回队头元素
    back()  返回队尾元素
    pop()  弹出队头元素

priority_queue, 优先队列,默认是大根堆
    size()
    empty()
    push()  插入一个元素
    top()  返回堆顶元素
    pop()  弹出堆顶元素
    定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;

stack, 栈
    size()
    empty()
    push()  向栈顶插入一个元素
    top()  返回栈顶元素
    pop()  弹出栈顶元素

deque, 双端队列
    size()
    empty()
    clear()
    front()/back()
    push_back()/pop_back()
    push_front()/pop_front()
    begin()/end()
    []

set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
    size()
    empty()
    clear()
    begin()/end()
    ++, -- 返回前驱和后继,时间复杂度 O(logn)

    set/multiset
        insert()  插入一个数
        find()  查找一个数
        count()  返回某一个数的个数
        erase()
            (1) 输入是一个数x,删除所有x   O(k + logn)
            (2) 输入一个迭代器,删除这个迭代器
        lower_bound()/upper_bound()
            lower_bound(x)  返回大于等于x的最小的数的迭代器
            upper_bound(x)  返回大于x的最小的数的迭代器
    map/multimap
        insert()  插入的数是一个pair
        erase()  输入的参数是pair或者迭代器
        find()
        []  注意multimap不支持此操作。 时间复杂度是 O(logn)
        lower_bound()/upper_bound()

unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
    和上面类似,增删改查的时间复杂度是 O(1)
    不支持 lower_bound()/upper_bound(), 迭代器的++,--

bitset, 圧位
    bitset<10000> s;
    ~, &, |, ^
    >>, <<
    ==, !=
    []

    count()  返回有多少个1

    any()  判断是否至少有一个1
    none()  判断是否全为0

    set()  把所有位置成1
    set(k, v)  将第k位变成v
    reset()  把所有位变成0
    flip()  等价于~
    flip(k) 把第k位取反

set(集合)

set,顾名思义,就是数学上的集合——每个元素最多只出现一次,并且set中的元素已经从小到大排好序。

常用函数

begin()        返回set容器的第一个元素的地址

end()      返回set容器的最后一个元素地址

clear()          删除set容器中的所有的元素

empty()     判断set容器是否为空

max_size()     返回set容器可能包含的元素最大个数

size()      返回当前set容器中的元素个数

erase(it)             删除迭代器指针it处元素

set的遍历

需要用到迭代器,具体的方法见下面的代码:

1 ##include <iostream>
 2 #include<set>
 3 using namespace std;
 4 
 5 
 6 int main()
 7 {
 8     set<int> s;                //创建一个int类型的set
 9  
10     s.insert(10);                //插入数据
11     s.insert(30);
12     s.insert(20);
13     s.insert(40);                
14 
15     //遍历数据,用迭代器遍历数据
16     for (set<int>::iterator it = s.begin(); it != s.end(); ++it)    
17     {
18         cout << *it << endl;
19     }
20     //这里用到了set中的元素已经从小到大排好序的性质
21     
22     return 0;
23 }

最后,不得不提的就是结构体类型(struct )的set ,使用时必须要重载 '<' 运算符

 1 #include<iostream>
 2 #include<set>
 3 #include<string>
 4 using namespace std;
 5 struct Info
 6 {
 7     string name;
 8     double score;
 9     bool operator < (const Info &a) const // 重载“<”操作符,自定义排序规则
10     {
11         //按score由大到小排序。如果要由小到大排序,使用“>”即可。
12         return a.score < score;
13     }
14 };
15 int main()
16 {
17     set<Info> s;
18     Info info;
19 
20     //插入三个元素
21     info.name = "Jack";
22     info.score = 80;
23     s.insert(info);
24     info.name = "Tom";
25     info.score = 99;
26     s.insert(info);
27     info.name = "Steaven";
28     info.score = 60;
29     s.insert(info);
30 
31     set<Info>::iterator it;
32     for(it = s.begin(); it != s.end(); it++)
33         cout << (*it).name << " : " << (*it).score << endl; 
34     return 0;
35 }

c++函数

floor函数向下取整
ceil函数向上取整
round函数四舍五入
tolower函数c++大写转小写
abs绝对值
fmod浮点数之间的取余
isdigit判断一个字符是否为数字

sort排序

)Sort函数包含在头文件为#include<algorithm>的c++标准库中,调用标准库里的排序方法可以不必知道其内部是如何实现的,只要出现我们想要的结果即可!

II)Sort函数有三个参数:

(1)第一个是要排序的数组的起始地址。

(2)第二个是结束的地址(最后一位要排序的地址)

(3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。

Sortt函数的第三个参数可以用这样的语句告诉程序你所采用的排序原则

less<数据类型>()//从小到大排序

greater<数据类型>()//从大到小排序
 

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
   vector<int>arr;
   
    for(int i=0;i<n;i++)
    {   int a;
        cin>>a;
        arr.push_back(a);
    }
    sort(arr.begin(),arr.end());
    for(vector<int>::iterator it=arr.begin();it!=arr.end();it++)
    {
        cout<<*it<<" ";
    }
    
    return 0;
}

或者

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
 
	int a[10]={9,6,3,8,5,2,7,4,1,0};
 	sort(a,a+10,greater<int>());
 	
 	for(int i=0;i<10;i++)
 	cout<<a[i]<<" ";
	return 0;
}

 等价与vector注意两者的使用方法

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin>>n;
   int a[n];
   
    for(int i=0;i<n;i++)
    {   
        cin>>a[i];
        
    }
    sort(a,a+n);
      for(int i=0;i<n;i++)
    {   
        cout<<a[i]<<" ";
        
    }
    
    return 0;
}

sort的进阶使用暨结构体的复习

#include<bits/stdc++.h>
using namespace std;

struct T {
	int h;
	int m;
	int s;
};
bool camp(T a,T b)
{
	return a.h*3600+a.m*60+a.s< b.h*3600+b.m*60+b.s;
}

int main(){
	int n;
	cin>>n;
	struct T arr[n];
	for(int i=0;i<n;i++)
	{
		cin>>arr[i].h>>arr[i].m>>arr[i].s;	
	
	}
	sort(arr,arr+n,camp);
	for(int i=0;i<n;i++)
	{
		cout<<arr[i].h<<" "<<arr[i].m<<" "<<arr[i].s<<endl;
	
	}
	
	
	
	return 0;
}

其他好用的

判断回文数

bool f(int x)
{
    string s=to_string(x);
    string t=s;
    reverse(t.begin(),t.end());
    if(s==t)return 1;
    return 0;

    
}

排除输入的小数的方法

字符串集体转小写的方法

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    for(char &c:s)
    {
        c=tolower(c);
    }
 
        int b=s.find("bob");
    if(b<0)cout<<"-1";
    else cout<<b;
}

  保留K位小数

#include<bits/stdc++.h>
using namespace std;
int main(){
double m,n,rt,p;
cin>>m>>n>>p;
rt=m*pow(2.718281828459045,(-0.114514*n));
cout<<fixed<<setprecision(p)<<rt;	
		
	return 0;
} 

字符串转换

stoi: string型变量转换为int型变量

stol: string型变量转换为long型变量

stoul:string型变量转换为unsigned long型变量

stoll: string型变量转换为long long型变量(常用)

stoull:string型变量转换为unsigned long long型变量

stof: string型变量转换为float型变量

stod: string型变量转换为double型变量(常用)

stold:string型变量转换为long double型变量

string s;
stoll(s1,nullptr,16);
//16进制转10进制
stoll(s,nullptr,2);
//2进制转10进制
nullptr表示空指针;

试除法

        for(int i=2;i<=x/i;i++)
        {
            if(x%i==0)
            {
               while(x%i==0)
               {
                   x=x/i;
               }
                cout<<i<<' ';
            }
        }
        if(x>1)
        {
            cout<<x<<endl;
        }else{
            cout<<endl;
        }
    }

可很好解决输出素因子时重复输出的问题;且数据之间不相互影响

何时停止输入


 while(scanf("%ld%ld",&a,&b)!=EOF)  当停止输入

判断闰年


if ((x % 4 == 0 && x % 100 != 0) || (x % 400 == 0))判断闰年

英文字符大小写转换函数

 
if (ch >= 'a' && ch <= 'z')

ch = toupper(ch);

else if (ch >= 'A' && ch <= 'Z')

ch = tolower(ch);

nth_element输出第K小的数
在强大的STL库中存在一个神奇的函数,那就是nth_element,这个函数主要用来将数组元素中第k小的整数排出来并在数组中就位,随时调用,可谓十分实用。

函数语句:nth_element(数组名,数组名+第k小元素,数组名+元素个数);

执行以后数组名[k]即第K小的数,左边的都比它小,右边的都比它大,但不保证有序。

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[5000010];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    nth_element(a,a+k,a+n);//使第k小整数就位 
    printf("%d",a[k]);//调用第k小整数

字符串转换

stoi: string型变量转换为int型变量

stol: string型变量转换为long型变量

stoul:string型变量转换为unsigned long型变量

stoll: string型变量转换为long long型变量(常用)

stoull:string型变量转换为unsigned long long型变量

stof: string型变量转换为float型变量

stod: string型变量转换为double型变量(常用)

stold:string型变量转换为long double型变量

string s;
stoll(s1,nullptr,16);
//16进制转10进制
stoll(s,nullptr,2);
//2进制转10进制
nullptr表示空指针;

 保留K位小数

#include<bits/stdc++.h>
using namespace std;
int main(){
double m,n,rt,p;
cin>>m>>n>>p;
rt=m*pow(2.718281828459045,(-0.114514*n));
cout<<fixed<<setprecision(p)<<rt;	
		
	return 0;
} 

	for(int i=1;cin>>s&&s!='E';i++)//循环读入,当读到字符E结束 

乒乓球比赛中11制位

玩过乒乓球大部分人都知道,正规乒乓球比赛,不仅分数要大于11(或21),两者分数相差也要大于2。如果比赛分数达到11-10,比赛会继续。直到一个人比另外一个人多两分。(如13-11

 

 

转移字符
更简单的方法是cin>>s;cout>>s;
转义字符是一种特殊的字符序列,用于表示无法直接键入或显示的字符,或者表示具有特殊含义的字符。在许多编程语言和文本处理环境中,包括C++,转义字符通常以反斜杠(\)后跟一个或多个字符的方式来表示。当你使用这些转义字符时,编译器或解释器会将它们替换为特定的字符或执行特定的操作。
以下是一些常见的C++转义字符和它们的含义:

1.\':单引号。用于在字符常量中表示单引号。

char singleQuote = '\'';


2.\":双引号。用于在字符串常量中表示双引号。

std::string doubleQuote = "This is a double quote: \"";


3.\\:反斜杠。用于在字符串中表示反斜杠字符自身。

std::string path = "C:\\Windows\\System32\\";


4.\n:换行。用于在字符串中插入换行符,使文本换行。

std::cout << "Hello,\nWorld!" << std::endl;


5.\t:制表符。用于在字符串中插入制表符,产生水平定位效果。

std::cout << "Name\tAge\tCity" << std::endl;


6.\r:回车。在某些情况下用于将光标移到行的开头。
7.\b:退格。用于在字符串中插入退格字符。
8.\a:响铃。在某些环境中用于触发响铃或提醒操作。

这些是一些常见的C++转义字符,但还有其他更多用于表示特殊字符或执行特定操作的转义字符。转义字符的使用可以帮助您在字符串和字符常量中表示各种特殊字符和控制字符,使您能够以更灵活的方式处理文本和字符数据。
 

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值