C++常用模板

Dev C++ 5.11版本下载链接

头文件框架

#include<iostream> //导入C++头文件
#include<stdio.h> //导入C语言头文件
#include<bits/stdc++.h> //导入万能头文件 
using namespace std; //使用命名空间,如果不写那么C++里面的函数就不能使用
int main() //主函数
{
    //程序在这里开始

    return 0; //主函数在这里结束
}//大括号表示主函数包含的代码

 

当然了,你或许还需要以下头文件

#include<bits/stdc++.h> //万能头
#include<math.h> //数学库
#include<string.h> //字符串库 

freopen模板

freopen(文件名,操作符,标准输入/输出):文件输入输出函数

文件名和操作符均为双引号字符串,标准输入stdin,标准输出stdout

freopen可以改写标准输入stdin,改成读取文件内容来输入

freopen可以改写标准输出stdout,改成写入内容到文件中输出

freopen("title.in","r",stdin); //改写标准输入stdin,改成"r"读取title.in文件里的内容作为输入
freopen("title.out","w",stdout);//改写标准输出stdout,改成将输出的内容通过写入"w",来写入到title.out文件中

 

 

C++常见的数据类型

 

 

常见的格式化符号

1 %c  一个字符(char)
2 %d  有符号十进制整数(int)(%ld、%Ld:长整型数据(long),%hd:输出短整形。)
3 %f  单精度浮点数(默认float)、%.nf  这里n表示精确到小数位后n位.十进制计数
4 %p  指针
5 %s  对应字符串char*(%s = %hs = %hS 输出 窄字符)
6 %%  打印一个百分号
7 %I64d 用于INT64 或者 long long
8 %I64u 用于UINT64 或者 unsigned long long

 C++的输入方法:scanf和cin

特别注意:scanf仅忽略空格,cin忽略空格和回车

// 使用scanf进行输入
int n;
scanf("%d", &n);

// 使用cin进行输入
int n;
cin >> n;

需要注意的是,scanf和cin的输入格式不同。scanf使用格式化字符串进行输入,而cin使用运算符>>进行输入。对于不同类型的变量,它们的输入格式也不同。下面是一些常见的输入格式:

// 输入一个整数
int n;
scanf("%d", &n);
cin >> n;

// 输入一个浮点数
double x;
scanf("%lf", &x);
cin >> x;

// 输入一个字符串
char s[100];
scanf("%s", s);
cin >> s;

分别输入一个整数int、小数double、字符char

int a;
double b;
char c;
cin>>a>>b>>c;
int a;
double b;
char c;
scanf("%d %f %c",&a,&b,&c);

输入字符型char字符串

1 char a[1001];
2 cin>>a; //遇到空格结束输入
3 scanf("%s\n",&a); //遇到空格结束输入
4 gets(a); //整行输入
5 cin.getline(a,100); //整行输入,最大输入100个字符

输入字符串型string字符串

1 string s;
2 cin>>s; //不能输入空格
3 getline(cin,s); //整行输入

 

C++输出模板cout和printf:

C++的输出模板通常使用printf和cout两种方式。其中,printf是C语言中的输出函数,而cout是C++中的输出流。下面是两种方式的示例代码:

// 使用printf进行输出
int n = 123;
printf("%d\n", n);

// 使用cout进行输出
int n = 123;
cout << n << endl;

需要注意的是,printf和cout的输出格式也不同。printf使用格式化字符串进行输出,而cout使用运算符<<进行输出。对于不同类型的变量,它们的输出格式也不同。下面是一些常见的输出格式:

// 输出一个整数
int n = 123;
printf("%d\n", n);
cout << n << endl;

// 输出一个浮点数,并保留2位小数
double x = 1.23;
printf("%.2lf\n", x);
cout << fixed << setprecision(2) << x << endl;

// 输出一个字符串
char s[] = "hello";
printf("%s\n", s);
cout << s << endl;

 

 

printf格式化输出保留小数模板

double n = 3.45;
printf("%.2f",n); //n保留2位小数 
double a = 3.1415926,b = 99;
printf("%.3f %.4f",a,b); //a保留3位小数 ,b保留4位小数 

 cout输出保留小数模板

double n = 3.42;
cout<<fixed<<setprecision(1);//设置输出的浮点数保留1位小数
cout<<n; //3.4

 

循环1到n的模板

1 for(int i=1;i<=n;i++)
1 int i = 1;
2 while(i<=n)
3 {
4     i++;
5 }

 

输入n个数/t组数据模板

例如,输入一个数n,接下来有n个数要输入

5

3 1 2 5 6

1 int n,x; cin>>n;
2 for(int i=1;i<=n;i++) //循环n遍 
3 {
4     cin>>x;
5 }
1 int n,x; cin>>n;
2 while(n--) //相当于n组数据
3 {   //注意,使用while循环结束后n会等于0 
4     cin>>x; 
5 }

 输入n个数存储数组模板

1 int a[1001],n;
2 cin>>n;
3 for(int i=1;i<=n;i++)cin>>a[i];

 字符数组的函数方法

定义

char a[1001]; //长度为1001的字符数组 

输入

cin>>a; //cin遇到空格或者回车换行会结束输入
gets(a); //整行输入,可以输入空格,遇到回车换行时结束 

strlen()获取字符串长度

char a[1001];
cin>>a;
int len = strlen(a); //获取a字符串的长度并赋值给len 

strcat(a,b)将b字符串拼接到a字符串后面

char a[1001],b[1001]; //定义a,b两个字符数组
cin>>a>>b; //输入两个字符串
cout<<strcat(a,b); //输出a和b拼接的结果

strcpy(a,b)将b字符串的内容复制到a中

strcmp(a,b)比较a和b两个字符串的大小,如果a>b则返回1,如果a<b则返回-1,如果a==b则返回0

字符串常用方法

string s;
s.size() /s.length() //表示字符串s的长度
s.substr(star,len) //表示从下标star开始,获取一个长度为len的子串

//获取以空格隔开的子串
int id = 0; //子串开始位置从0开始
for(int i=0;i<s.size();i++)
{
    if(s[i]是空格)
    {
        string str = s.substr(id,i-id); //获取从下标id开始,长度为i-id的子串str 
        id = i+1; //更新新的子串位置 
    }
} 

 

常见的数学库函数

abs(x) 计算x的绝对值
sqrt(x) 计算x的平方根
pow(a,b) 计算a的b次方

 

常见的时间复杂度

https://www.cnblogs.com/jyssh/p/17688352.html

 

 前缀和与差分

设原数组a[],前缀和数组s[],差分数组d[]
前缀和数组s[i] = s[i-1]+a[i]
区间前缀和:求l到r的区间和 s[r]-s[l-1]

差分数组d[i] = a[i]-a[i-1]
差分区间l到r增加p d[l]+=p,d[r+1]-=p
差分还原回原数组a:a[i] = d[i]+a[i-1] 
View Code

 

分割整数算法

int sum(int x) //求x的各个位数和
{
    int res = 0;
    while(x>0)
    {
        res += x%10; //加上个位数 
        x/=10; //舍弃个位数 
    }
    return res;
}
View Code

 

进制转化代码

1. 任何一个进制转化成另一个进制都要通过10进制作为中介,除了10进制本身

2.如2转8,需要2->10->8; 反之8转2也要经过8->10->2

3.因此,在这里dc函数是将任何进制转换成10进制,rdc函数是将10进制转换成任何进制

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char g[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
char a[1001],s[1001]; //10进制转成x进制后会存储到a中 
//输入的非10进制字符串s
ll dc(int x) //x进制转成10进制 
{
    ll len = strlen(s),k = 0,n = 0;
    for(int i=len-1;i>=0;i--)
    {
        int o;
        if(s[i]>'9')o = s[i]-'A'+10;
        else o = s[i]-'0';
        n = n+o*pow(x,k);
        k++;
    }
    return n; //返回十进制数n    
}
int rdc(ll n,int x) //十进制转成x进制 
{
    if(n==0)
    {
        a[1] = '0';return 1; //以防n==0的情况 
    }
    int len = 0;
    while(n>0)
    {
        a[++len] = g[n%x]; //将n%x的结果存储
        n/=x; 
    }
    return len;
}
int main()
{
    cin>>s;
    ll n = dc(16); //获取16进制转成十进制n 
    //cout<<n<<endl;
    int len = rdc(n,2); //获取n转成2进制后的长度len,数据存储在a数组中 
    for(int i=len;i>=1;i--)cout<<a[i]; //从len到1就是n转成16进制的结果 
     return 0;
}
View Code

一、 2-36进制转十进制方法:strtol()函数

函数原型:long int strtol(const char *nptr, char **endptr, int base)

base是要转化的数的进制,非法字符会赋值给endptr,nptr是要转化的字符

#include <bitset>
#include<iostream>
using namespace std;
int main()
{
    char buffer[20]="10549stend#12";
    char *stop;
    int ans=strtol(buffer,&stop,8);  //将八进制1054转成十进制,后面均为非法字符
    cout<<ans<<endl;
    cout<<stop<<endl;
    return 0;
}
/*
556
9stend#12
*/
View Code

注意:

①如果base为0,且字符串不是以0x(或者0X)开头,则按照十进制进行转化。

②如果base为0或者16,并且字符串以0X(或者0x)开头,则x(或者X)被忽略,字符串按16进制转化。

③如果base不等于0和16,并且字符串以0x(或者0X)开头,则x被视为非法字符。

④对于nptr指向的字符串,其开头和结尾的空格被忽略,字符串中间的空格被视为非法字符。

二、 将10进制数转换为任意的n进制数,结果为字符串类型

 itoa()函数(可以将一个10进制数转换为任意的2-36进制字符串)

函数原型:char* itoa(int value, char* string, int radix);

例如:itoa(num,str,2); num是一个int型的,是要转换的10进制数,str是转换结果,后面的值为目标进制。

#include<cstdio> 
#include<cstdlib> 
int main()  
{  
    int num = 10;  
    char str[100];  
    _itoa(num, str, 2);  //c++中一般用_itoa,用itoa也行,
    printf("%s\n", str);  
    return 0;  
}
View Code

 

位移符 左移<< 和 右移>>

在C++中,位移符主要有两种:左移符(<<)和右移符(>>)。它们用于将二进制数的位向左或向右移动指定的位数。以下是关于这两种位移符的详细介绍:
1. 左移符(<<):将一个数的二进制表示向左移动指定的位数,右侧用0填充。例如,a << b表示将a的二进制表示向左移动b位。这相当于将a乘以2^b。

   int a = 4; // 二进制表示:0100
   int b = 2;
   int result = a << b; // 结果为:10000(十进制表示:16)

2. 右移符(>>):将一个数的二进制表示向右移动指定的位数。对于无符号整数,左侧用0填充;对于有符号整数,左侧用符号位填充(即正数用0填充,负数用1填充)。例如,a >> b表示将a的二进制表示向右移动b位。这相当于将a除以2^b(向下取整)。

   int a = 16; // 二进制表示:10000
   int b = 2;
   int result = a >> b; // 结果为:0100(十进制表示:4)

左移符1<<k计算2的k次方

可以使用左移符(<<)将数字1向左移动k位。具体来说,表达式1 << k将计算出2的k次方。
例如,如果k等于3,那么1 << k的计算过程如下:

1. 将数字1转换为二进制:0001
2. 将二进制数向左移动3位:1000
3. 将结果转换回十进制:8

因此,1 << 3的结果是8,即2的3次方。

以下是一个C++示例,展示了如何使用位移符计算2的k次方:

#include <iostream>

int main() {
    int k = 3;
    int result = 1 << k;
    std::cout << "2^" << k << " = " << result << std::endl;

    k = 5;
    result = 1 << k;
    std::cout << "2^" << k << " = " << result << std::endl;

    return 0;
}
View Code

这段代码将输出:

2^3 = 8
2^5 = 32
View Code

 

深度优先搜索

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10,inf = 0x3f3f3f3f;
int mp[N][N]; //地图 
int vis[N][N]; //标记数组
int dx[8] = {1,0,0,-1,-1,-1,1,1};
int dy[8] = {0,1,-1,0,-1,1,-1,1}; //方向数组,下右左上
int n,m,f,ans; //n行m列,f终点标记,ans路径数 
int sx,sy,ex,ey; //起点坐标、终点坐标
void dfs(int x,int y) //在x和y这个点进行搜索 
{
    if(x==ex && y==ey) //找到宝箱
    {
        ans++; //路径数+1 
        return ;
    } 
    for(int i=0;i<4;i++)
    {
        int tx = x + dx[i]; //下一个方向行坐标 = 当前坐标x + 第i个方向的行增量
        int ty = y + dy[i];
        //越界判断
        if(tx<1 || tx>n || ty<1 || ty>m)continue;
        //判断tx、ty是否可行
        if(mp[tx][ty]!=1 && vis[tx][ty]==0) //tx、ty没有蝙蝠且没有走过
        {
            vis[tx][ty] = 1; //标记tx、ty,证明走过
            dfs(tx,ty); //以tx、ty作为起点继续搜索
        } 
    }
}
int main()
{
    //1.输入地图
    cin>>n>>m; //n*n地图
    for(int i=1;i<=n;i++) //i循环行数 
        for(int j=1;j<=m;j++) //j循环列数 
            cin>>mp[i][j];
    cin>>ex>>ey;
    //从起点开始搜索
    if(mp[1][1]==1 || mp[ex][ey]==1)//门口有蝙蝠的情况 
    {
        cout<<"NO";return 0;
    }
    vis[1][1] = 1; //标记起点 
    dfs(1,1);
    //输出答案
    if(ans)cout<<"YES";
    else cout<<"NO"; 
     return 0;
}
View Code

 

 广度优先搜索

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10,inf = 0x3f3f3f3f;
int mp[N][N];
int vis[N][N];
int nex[4][2] = {{0,1},{1,0},{0,-1},{-1,0}}; //右下左上方向
int n,m,ans = -1,sum,f; //n行m列,f终点标记,ans答案
struct node{
    int x,y,step; //坐标(x,y),当前走了step步 
};
void bfs(int sx,int sy,int ex,int ey) //能得到从(sx,sy)到(ex,ey)的最短路ans 
{
    queue<node> q; //创建空队列
    node t;
    //初始化起点入队
    t.x = sx; t.y = sy; t.step = 0; 
    q.push(t); //起点入队
    vis[sx][sy] = 1; //标记起点已走过
    while(!q.empty()) //队列非空时执行循环
    {
        //获取队首并让队首出队
        node head = q.front(),tail; q.pop(); 
        for(int i=0;i<4;i++) //选择方向
        {
            //获取下一步坐标 = 队首head的坐标+nex第i个方向的增量 
            int tx = head.x + nex[i][0]; 
            int ty = head.y + nex[i][1]; 
            //越界判断注意边界从0还是1开始,默认从1开始 
            if(tx<1 || tx>n || ty<1 || ty>m)continue;
            //判断下一步能不能走,有没有走过
            if(mp[tx][ty]!=1 && vis[tx][ty] == 0)
            {
                //结点(tx,ty)入队并标记  队尾的步长 = 队首的步长+1 
                tail = {tx,ty,head.step+1};
                vis[tx][ty] = 1; //标记结点(tx,ty) 
                q.push(tail); //结点tail入队 
            }
            if(tx==ex && ty==ey) //判断当前结点是否为终点
            {
                f = 1;
                ans = tail.step; //队尾的步长是起点到终点的最短路 
                break;
            } 
        }
        if(f)break; //结束搜索 
    } 
} 
int main()
{
    //输入n行m列地图,捏造起点终点 
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>mp[i][j];
    int sx,sy,ex,ey;
    cin>>sx>>sy>>ex>>ey;
    //传入起点终点,进行搜索
    bfs(sx,sy,ex,ey);
    //输出答案
    cout<<ans; 
     return 0;
}
View Code

 

 埃氏筛

#include<bits/stdc++.h>
#define f(i,s,e) for(int i = s; i <= e; i++)
#define ll long long
using namespace std;
const int N = 1e7+10,inf = 0x3f3f3f3f;
int vis[N];
int p[N],k;
void alsh(int n)
{
    for(int i = 2; i <= n; i++)
    {
        if(vis[i] == 0)
        {
            p[++k] = i;
            for(int j = i * 2; j <= n; j+=i)
                vis[j] = 1;
        }
    }
}
int main()
{
    
    alsh(1e6);
    for(int i = 1; i <= k; i++)
        cout << p[i] << endl;
    return 0;
}
View Code

 

 欧拉线性筛素数

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7;
bool vis[N] = {1,1};
int p[500001],k; //可以获得总共k个素数存在p数组里
void ola()
{
    for(int i=2;i<=N;i++) //找出2到N中所有的素数 
    {
        if(vis[i]==0)p[++k] = i;
        for(int j=1;j<=k&&p[j]*i<=N;j++)
        {    
            vis[p[j]*i] = 1;
            if(i%p[j]==0)break;
        }
    }
}
int main()
{
    ola();
     return 0;
}
View Code

 

dijkstra算法模板

特别注意minn==inf的判断要写,不然很容易RE

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int g[2005][2005],vis[2005],dis[2005];
//g[i][j]表示从i到j的距离
//vis[i]表示标记第i点作为中间点
//dis[i]表示起点到i的最短距离
int n,m,t;
void dij(int s) //s起点
{
    for(int i=1;i<=n;i++)dis[i] = g[s][i]; //初始化起点到i的距离为g[s][i]
    dis[s] = 0;vis[s] = 1; //标记起点,初始化到地点的距离为0
    int minn,pos; //pos是没有标记过的从起点出发可到达的最短距离的点
    for(int i=1;i<=n;i++)
    {
        minn = inf;
        for(int j=1;j<=n;j++)
        {
            if(vis[j]==0&&dis[j]<minn)
            {
                minn = dis[j];pos = j; //寻找可以更新最短路的点
            }
        }
        if(minn==inf)break; //如果没有更新成功,那么接下来也不会更新了
        vis[pos] = 1;
        for(int j=1;j<=n;j++)
        {
            if(dis[j]>dis[pos]+g[pos][j])
                dis[j] = dis[pos]+g[pos][j]; //以pos为中间点,更新最短路的所有点
        }
    }
}
View Code

 

floyd算法模板

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int g[2005][2005]; //g[i][j]表示从i到j的最短距离
int n,m,t;
void floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(g[i][j]>g[i][k]+g[k][j]) //如果地图g上从i到j的距离大于从i到k+k到j的距离
                    g[i][j] = g[i][k]+g[k][j]; //更新最短距离
}
View Code

 

 spfa算法模板

5778: 城市路 dijkstra/spfa(邻接矩阵/邻接表)

并查集模板

int f[N]; //f[x] = y 表示x的父结点是y

for(int i=1;i<=n;i++)f[i] = i;//初始化每个人的祖先是自己 

int find(int x) //查找,寻找x的祖先节点,并将f[x]更改为祖先 
{
    if(f[x]!=x)f[x] = find(f[x]);
    return f[x];
}

void merger(int x,int y) //合并,将x合并到y 
{
    int fx = find(x);
    int fy = find(y);
    f[fx] = fy; //合并的是x和y的祖先 
}
View Code

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10,inf = 0x3f3f3f3f;
int n,m;//n个点,m个关系
int f[N],a[N];//f[i] = x表示i的祖先是x ,a[i] = x i所在的关系网有x人 
int find(int x) //找到x的祖先 x=2  f[x] = 3
{
    if(f[x]!=x)f[x] = find(f[x]); //如果x的爹不是自己,那么去寻找爹中爹
    return f[x];
} 
void merger(int x,int y) //合并x和y两个集合
{
    int fx = find(x);
    int fy = find(y);
    if(a[fx]<a[fy])swap(fx,fy); //交换位置,保证fx所在的关系网人数是比较多的 
    a[fx] = a[fx] + a[fy];
    f[fy] = fx; //fy的祖先是fx 
} 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)f[i] = i,a[i] = 1; //最开始的时候,每个人的祖先是自己
    for(int i=1;i<=m;i++)
    {
        int x,y; cin>>x>>y; //x和y是认识的
        if(find(x)!=find(y)) //如果x的祖先和y的祖先是不认识的 
            merger(x,y); //那么合并x和y的关系 
    } 
    cout<<"并查集结束后的关系网:"<<endl;
    for(int i=1;i<=n;i++)
    {
        printf("%d的祖先是%d,%d所在的家族人数为%d\n",i,f[i],i,a[find(i)]); 
    }
    int sum = 0,maxx = 0; //总共有sum个家族,家族最大人数为maxx
    for(int i=1;i<=n;i++)
    {
        if(f[i]==i)sum++;
        maxx = max(maxx,a[find(i)]); //比较maxx和第i个家族人数的大小 
    } 
    printf("共有%d个家族,家族最大人数为%d\n",sum,maxx);
     return 0;
}
View Code

 

最小生成树prim&&kruskal

prim:Prim算法是一种贪心算法,它从一个顶点开始,逐步扩展生成树,直到生成整个图的最小生成树。具体步骤如下:

1. 选取一个起始顶点,将其加入生成树中。
2. 找到与生成树相邻的所有顶点中,权值最小的边所连接的顶点,将其加入生成树中。
3. 重复步骤2,直到生成整个图的最小生成树。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,inf=0x3f3f3f3f;
int dis[N],vis[N],g[N][N],f[N];
//dis[i]表示第i个点在生成树中的最小权值
//vis[i]表示第i个点是否加入了最小生成树
//f[i]表示和第i个点相连的点
//g[i][j]表示从i到j的距离 
int n,m,ans; //n个点,m条边 
void Prim() 
{
    memset(vis,0,sizeof(vis));
    ans = 0; //最小生成树的代价 
    vis[1]=1;
    for(int i=2;i<=n;i++)dis[i]=g[1][i],f[i]=1;
    for(int i=2;i<=n;i++)
    {
        int minc=inf;
        int p=-1;
        for(int j=1;j<=n;j++)
            if(!vis[j]&&minc>dis[j])
                minc=dis[j],p=j;
        if(minc==inf)break; 
        vis[p]=1;
        //printf("%d %d %d\n",f[p],p,dis[p]);//输出p点在生成树的最小边和权值
        ans+=dis[p]; //加上权值 
        for(int j=1;j<=n;j++)
            if(!vis[j]&&dis[j]>g[p][j])
            {
                   dis[j]=g[p][j],f[j]=p;             
            }
    }
}
int main()
{
    cin>>n>>m;
    memset(g,inf,sizeof g); //初始化地图的为极大值 
    for(int i=1;i<=m;i++) //输入m条边构图 
    {
        int x,y,z;cin>>x>>y>>z;
        g[x][y] = g[y][x] = z;
    }
    Prim(); //调用普利姆算法 
    cout<<ans; //输出权值 
    return 0;
}
View Code

 

 kruskal:Kruskal算法也是一种贪心算法,它将所有边按照权值从小到大排序,然后依次加入生成树中,直到生成整个图的最小生成树。具体步骤如下:
1. 将所有边按照权值从小到大排序。
2. 依次加入每条边,如果加入后形成环,则不加入。
3. 重复步骤2,直到生成整个图的最小生成树。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10,inf = 0x3f3f3f3f;
struct node{
    int x,y,z;
}a[N];
int f[N];
int n,m;
bool cmp(node a,node b)
{
    return a.z<b.z; //按权值z从小到大 
}
int find(int x)
{
    if(f[x]!=x)f[x] = find(f[x]);
    return f[x];
}
void merger(int x,int y)
{
    int fx = find(x);
    int fy = find(y);
    f[fy] = fx;
}
void kruaskal()
{
    for(int i=1;i<=n;i++)f[i] = i; //并查集初始化 
    sort(a+1,a+1+m,cmp); //对m条边进行按权值从小到大排序 
    int sum = 0,ans = 0; //sum是当前生成树中边的数量,ans是当前生成树的最小权值 
    for(int i=1;i<=m;i++)
    {
        if(find(a[i].x)!=find(a[i].y)) //如果x,y两个点未连接 
        {
            sum++;
            ans+=a[i].z;
            merger(a[i].x,a[i].y); //合并
            printf("%d %d %d\n",a[i].x,a[i].y,a[i].z);
            if(sum==n-1)break;//只需要n-1条边即可生成树 
        }
    }
    cout<<ans<<endl;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)cin>>a[i].x>>a[i].y>>a[i].z;
    kruaskal();
     return 0;
}
View Code

 

拓扑排序

拓扑排序是一种对有向无环图进行排序的算法。它的基本思想是将图中的节点按照它们之间的依赖关系进行排序,使得每个节点都排在它的后继节点之前。这种排序可以用来解决很多实际问题,比如任务调度、编译顺序等。

在实现拓扑排序时,可以使用一个队列来存储入度为0的节点。每次从队列中取出一个节点,并将它的后继节点的入度减1。如果某个节点的入度变为0,就将它加入队列中。重复这个过程,直到队列为空为止。最终得到的序列就是拓扑排序的结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4+10,inf = 0x3f3f3f3f;
vector<int>g[N];
int n,m,x,y,rd[N];
queue<int>q; 
int topsort()
{
    int num = 0;
    for(int i=0;i<n;i++)if(rd[i]==0)q.push(i);//先将入度为0的点加入队列 
    while(!q.empty())
    {
        int x = q.front();q.pop();num++; //队首出队,数量+1 
        for(int i=0;i<g[x].size();i++) //遍历和队首x相邻的点g[x] 
        {
            rd[g[x][i]]--; //将和队首x相邻的点的入度-1 
            if(rd[g[x][i]]==0)q.push(g[x][i]); //如果出现入度为0的点,就入队 
        }
    }
    if(num==n)return 1; //如果在队列中刚好所有点都出现了,那么就证明无环 
    else return 0;
}
int main()
{
    while(cin>>n>>m)
    {
        if(!n && !m)break;
        for(int i=0;i<n;i++)g[i].clear();
        memset(rd,0,sizeof(rd));
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=m;i++)
        {
            cin>>x>>y;
            g[x].push_back(y);rd[y]++;
        }
        if(topsort()) //返回0证明存在环 
        {
            cout<<"YES"<<endl;
        }
        else cout<<"NO"<<endl;
    }
     return 0;
}
View Code

 

单调队列dp

求n个数中长度为k的滑动窗口中的最小值、最大值

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxn 1000100
using namespace std;
int q[maxn], a[maxn];
int n, k;

void getmin() {  // 得到这个队列里的最小值,直接找到最后的就行了
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] >= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}

void getmax() {  // 和上面同理
  int head = 0, tail = 0;
  for (int i = 1; i < k; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
  }
  for (int i = k; i <= n; i++) {
    while (head <= tail && a[q[tail]] <= a[i]) tail--;
    q[++tail] = i;
    while (q[head] <= i - k) head++;
    printf("%d ", a[q[head]]);
  }
}

int main() {
  scanf("%d%d", &n, &k);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  getmin();
  printf("\n");
  getmax();
  printf("\n");
  return 0;
}
View Code

 

树状数组

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10,inf = 0x3f3f3f3f;
ll c[N];
//c是树状数组,其中c[i] = a[i-2^k+1]+...+a[i](k为在i的二进制形式下末尾0的个数)
int n,m,k,a,b;
int lowbit(int x) //用lowbit(x)表示2^k k为x在二进制形式下末尾0的个数 
{
    return x&(-x);
} 
void updata(int x,int v) //在序列第x个位置上加上v,并在树状数组中修改元素
{
    while(x<=n)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
} 
ll sum(int x) //计算第1个数到第x个数的和
{
    ll res = 0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
} 
int main()
{
    int v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v);
        updata(i,v);
        //开始每个元素初始值为0,我们读入数列时直接在对应的位置增加v即可 
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&k,&a,&b);
        if(k==2)
            printf("%lld\n",sum(b)-sum(a-1));
            //计算区间[a,b]的和时,可以分别算出[1,b]和[1,a-1]的和,相减即为[a,b]的和
        else
            updata(a,b); //在第a个位置上加上b 
    }
     return 0;
}
View Code

 

RMQ求区间最大最小值

输入一串数字,给你 M 个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y 这段区间内的最大数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+10,inf = 0x3f3f3f3f,lgn = 20;
int lg[N],f[N][lgn+5],a[N];
int n,m,x,y;
void rmq()
{
    lg[0] = -1;
    for(int i=1;i<=n;i++)
        f[i][0] = a[i],lg[i] = lg[i>>1]+1; //预处理出长度为1-n的lg值
    for(int j=1;j<=lgn;j++) //计算f[i][j]
        for(int i=1;i+(1<<j)-1<=n;i++) //区间边界不超过n
            f[i][j] = max(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    rmq(); //预处理rmq 
    while(m--)
    {
        scanf("%d%d",&x,&y);
        int s = lg[y-x+1]; //求lg2(y-x+1)下取整的值 
        printf("%d\n",max(f[x][s],f[y-(1<<s)+1][s]));
    } 
     return 0;
}
View Code

 

线段树模板链接

https://www.cnblogs.com/jyssh/p/17685734.html

 

KMP模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+10,inf = 0x3f3f3f3f;
int nex[N]; //nex[j]的意思是当子串的第j个字符和主串的第i个字符不匹配时,我们应该从子串的nex[j]字符开始重新匹配 
string a,b;
/*
kmp指针回退 j = nex[j - 1]
根本原因:子串的指针j总是表示我们即将要匹配子串的第j个字符,所以如果子串[j]和主串[i]不匹配
那么我们自然是要从子串的nex[j - 1]字符开始重新匹配
*/
void get_next(string b)
{
    int j = 0; //前缀末尾
    for(int i = 1; i < b.size(); i++)
    {
        //指针回退
        while(j > 0 && b[i] != b[j]) j = nex[j - 1];
        //前缀和后缀的末尾相同则计数 
        if(b[j] == b[i]) j++;
        nex[i] = j; 
    } 
}
int kmp(string a,string b) //获取子串在主串中第一次出现的位置
{
    int j = 0; //子串指针j,主串指针i 
    for(int i = 0; i < a.size(); i++)
    {
        while(j > 0 && a[i] != b[j]) j = nex[j - 1]; //3.指针回退 
        if(a[i] == b[j]) j++; //1.当前子串和主串匹配,那么继续匹配子串的下一个 
        if(j == b.size()) //2.如果匹配完了,那么成功匹配的位置是:主串当前长度i - 子串长度b.size() + 1
        {
            //这里是返回子串在主串中第一次出现的位置,如果要解决其他问题,可以在这里进行修改 
            return i - b.size() + 1; 
        }
    }
    return -1; //没有匹配成功返回-1 
}
int main()
{
    getline(cin,a);
    getline(cin,b);
    get_next(b);
    cout << kmp(a,b);
    return 0;
}
View Code

 

posted @ 2023-04-09 23:54  CRt0729  阅读(493)  评论(0编辑  收藏  举报