首页 > 编程语言 >ACM竞赛常用算法模板(C++)

ACM竞赛常用算法模板(C++)

时间:2023-11-05 18:37:39浏览次数:41  
标签:输出 int sum namespace ACM C++ 整数 include 模板


1.快速排序

AcWing 785.快速排序:https://www.acwing.com/problem/content/787/
题目描述

给定你一个长度为 n 的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整个数列。

输出格式

输出共一行,包含 n个整数,表示排好序的数列。

数据范围

1≤n≤100000

代码如下
#include <iostream>
using namespace std;
const int N = 1000010;
int q[N];
void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;
	int i = l - 1, j = r + 1, x = q[l + r >> 1];
    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);
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
	quick_sort(q, 0, n - 1);
	for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);

    return 0;
}

2.二分查找

洛谷P2249 【深基13.例1】查找:https://www.luogu.com.cn/problem/P2249
题目描述

输入 n 个不超过 109 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a1,a2,…,an,然后进行 m 次询问。对于每次询问,给出一个整数 q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1 。

输入格式

第一行 2 个整数 nm,表示数字个数和询问次数。

第二行 n 个整数,表示这些待查询的数字。

第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。

输出格式

输出一行,m 个整数,以空格隔开,表示答案。

数据范围

1≤n≤106,0≤ai,q≤109,1≤m≤105

代码如下
#include<cstdio>
using namespace std;
int n,m,q,a[1000005];
int find(int x) //二分查找 
{
	int l=1,r=n;
	while (l<r)
	{
		int mid=l+(r-l)/2;
		if (a[mid]>=x) r=mid;
		else l=mid+1;
	}
	if (a[l]==x) return l; //找都了就输出他的位置 
	else return -1; // 没找到输出-1 
}
int main()
{
	scanf("%d %d",&n,&m); //读入 
	for (int i=1 ; i<=n ; i++)
	    scanf("%d",&a[i]); //还是读入 
	for (int i=1 ; i<=m ; i++)
	{
		scanf("%d",&q);
		int ans=find(q); //看看查找的结果 
		printf("%d ",ans); //输出 
	}
	
	return 0;
}

3.二分答案

洛谷P1873 [COCI 2011/2012 #5] EKO / 砍树:https://www.luogu.com.cn/problem/P1873
题目描述

伐木工人 Mirko 需要砍 M 米长的木材。对 Mirko 来说这是很简单的工作,因为他有一个漂亮的新伐木机,可以如野火一般砍伐森林。不过,Mirko 只被允许砍伐一排树。

Mirko 的伐木机工作流程如下:Mirko 设置一个高度参数 H(米),伐木机升起一个巨大的锯片到高度 H,并锯掉所有树比 H 高的部分(当然,树木不高于 H 米的部分保持不变)。Mirko 就得到树木被锯下的部分。例如,如果一排树的高度分别为 20,15,10 和 17,Mirko 把锯片升到 15 米的高度,切割后树木剩下的高度将是 15,15,10 和 15,而 Mirko 将从第 1 棵树得到 5 米,从第 4 棵树得到 2 米,共得到 7 米木材。

Mirko 非常关注生态保护,所以他不会砍掉过多的木材。这也是他尽可能高地设定伐木机锯片的原因。请帮助 Mirko 找到伐木机锯片的最大的整数高度 H,使得他能得到的木材至少为 M 米。换句话说,如果再升高 1 米,他将得不到 M 米木材。

输入格式

第 1 行 2 个整数 NMN 表示树木的数量,M 表示需要的木材总长度。

第 2行 N 个整数表示每棵树的高度。

输出格式

1个整数,表示锯片的最高高度。

数据范围

对于 100% 的测试数据,1≤N≤106,1≤M≤2×109,树的高度 <109,所有树的高度总和 >M

代码如下
#include<bits/stdc++.h>
using namespace std;
long long n,bz,s=0,mid,leftt,longest,trees[1000008];
int main()
{
    scanf("%lld%lld",&n,&bz); 
    for(int i=1;i<=n;i++) 
    {
        scanf("%lld",&trees[i]);
        longest=max(longest,trees[i]);//找到最长木材 
    }
    while(leftt<=longest)
    {
        mid=(leftt+longest)/2; //从中间点开始作为伐木机高度
        s=0; 
        for(int i=1;i<=n;i++) 
			if(trees[i]>mid) //树的高度大于伐木机高度 
				s+=trees[i]-mid; //高的部分累加 
        if(s<bz) //木材不足 
			longest=mid-1;//在左边搜 减小高度增加木材 
		else 
			leftt=mid+1;//在右边搜 增加高度减小木材 
    }
    cout<<leftt-1; 
    return 0;
}

4.素数筛

洛谷P5736 【深基7.例2】质数筛:https://www.luogu.com.cn/problem/P5736
题目描述

输入 n 个不大于 105 的正整数。要求全部储存在数组中,去除掉不是质数的数字,依次输出剩余的质数。

输入格式

第一行输入一个正整数 n,表示整数个数。

第二行输入 n 个正整数 ai,以空格隔开。

输出格式

输出一行,依次输出 ai 中剩余的质数,以空格隔开。

数据范围

1≤n≤100,1≤ai≤105

代码如下
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
bool isprime(int x){//判断是否素数
	if(x<=1) return false;//如果小于2,一定不是素数
	for(int i=2;i<=sqrt(x);i++){//为什么要到sqrt(x)呢,因为如果有一个大于sqrt(n)的数可以被n整除,那么必有一个数n/i也可以被n整除且小于i
		if(x%i==0) return false;//如果可以整除,那么不是素数
	}
	return true;//是素数
}
int main(){
	int n,a;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a;
		if(isprime(a)){
			cout<<a<<" ";//是素数,就输出
		}
	}
	return 0;
}

5.整数数位分解

题目描述

输入一个数x,从个位开始输出每一位对应的值。

输入描述

输入一个数x。

输出描述

从个位开始输出每一位。

代码如下
#include<iostream>
using namespace std;
int a[10010],cnt=0;
void solve(int x)
{
    while(x)
    {
        a[cnt++]=x%10;
        x/=10;
    }
}
int main()
{
    int n;
    cin>>n;solve(n);
    for(int i=0;i<cnt;i++)
    {
        cout<<a[i]<<" ";
    }
    return 0;
}

6.质因数分解

题目描述

输入一个数,从小到大输出这个数的所有因数。

输入描述

输入一个数n。

输出描述

从小到大输出n的因数。

代码如下
#include<iostream>
using namespace std;
int a[10010],cnt=0;
void solve(int x)
{
    for(int i=1;i<=x;i++)
    {
        if(x%i==0)
        {
            a[cnt++]=i;
        }
    }
}
int main()
{
    int n;
    cin>>n;solve(n);
    for(int i=0;i<cnt;i++)
    {
        cout<<a[i]<<" ";
    }
}

7.gcd(最大公约数)&lcm(最小公倍数)

最大公约数gcd(the Greatest Common Divisor)

求gcd常用欧几里得算法( 辗转相除法 )

  • 递归式: gcd(a,b) = gcd(b,a%b)
  • 递归边界: gcd(a,0) = a
int gcd( int a, int b )
{
    return b ? gcd(b,a%b) : a;
}

证明:

a = kb + r, 则 r = a - kb.

d为a和b的一个公约数, 那么由 r = a - kb, 得d也是r的一个约数.

因此dbr的公约数.

又由 r = a%b, 得dba%b的公约数.

因此d既是a和b的公约数, 又是ba%b的公约数.

题目:求两个整数最大公约数,即每次将较大的数变成大数减去小数的值。

运用三目运算符“a?b:c”,若a为真,则返回b值,否则返回c值。

#include<bits/stdc++.h>
using namespace std;
int a,b;
int gcd(int x,int y)
{
	return y=0 ? x : gcd(y ,x%y);	//如果y的值为0,则返回x的值,否则返回gcd(y,x%y); 
}
int main()
{
	cin>>a>>b;
	cout<<gcd(a,b);
	return 0;
}
最小公倍数lcm(the Least Common Multiple)
int lcm( const int a, const int b )
{
    return a / gcd(a,b) * b;
}

最小公倍数的求解在最大公约数的基础上运行. 当得到a和b的最大公约数d之后, 可以马上得到a和b最小公倍数是ab / d.

由于ab在实际运算中可能会溢出,因此更恰当的写法是a / d \* b.由于d是a的约数,所以a/d一定可以整除.


8.前缀和

引例

给出5个数(1,3,7,5,2),求对应位的前缀和。

#include<iostream>
using namespace std;
int main()
{
	const int n = 5;
	int arr[n] = { 1,3,7,5,2 };
	int sum[n];
	sum[0] = arr[0];
	for (int i = 1; i < n; i++)sum[i] = sum[i - 1] + arr[i];
	for (int i = 0; i < n; i++)cout << sum[i] << " ";
	return 0;
}

输出:

1 4 11 16 18
自定义get_sum函数get_sum(int l,int r)

例如,给定5个数(1,3,7,5,2),求2-4,0-3,3-4位的前缀和。

#include<iostream>
using namespace std;
#define get_sum(l,r)(l?sum[r]-sum[l-1]:sum[r])
int main()
{
	const int n = 5;
	int arr[n] = { 1,3,7,5,2 };
	int sum[n];
	sum[0] = arr[0];
	for (int i = 1; i < n; i++)sum[i] = sum[i - 1] + arr[i];
	cout << get_sum(2, 4) << endl;
	cout << get_sum(0, 3) << endl;
	cout << get_sum(3, 4) << endl;

}

输出:

14
16
7

get_sum()函数也可这样定义:

#include<iostream>
using namespace std;
const int n = 5;
int sum[n];
int get_sum(int l, int r)
{
	if (l != 0)return sum[r] - sum[l - 1];
	else return sum[r];
}
int main()
{
	int arr[n] = { 1,3,7,5,2 };
	sum[0] = arr[0];
	for (int i = 1; i < n; i++)sum[i] = sum[i - 1] + arr[i];
	cout << get_sum(2, 4) << endl;
	cout << get_sum(0, 3) << endl;
	cout << get_sum(3, 4) << endl;
}

sum[0]=arr[0]; 类似数列第一项S1=a1

sum[i]=sum[i-1]+arr[i]; 类似数列an=Sn-Sn-1


9.差分

引例

给定5个数(1,3,7,5,2),定义一个函数add(int l,int r,int v) 使第l到第r位加v,输出最终数列。

#include<iostream>
using namespace std;
int d[6] = { 0 };
void add(int l, int r, int v)
{
	d[l] += v;
	d[r + 1] -= v;
}
int main()
{
	int arr[5] = { 1,3,7,5,2 };
	add(2, 4, 5);
	add(1, 3, 2);
	add(0, 2, -3);
	for (int i = 1; i < 5; i++)d[i] += d[i - 1];
	for (int i = 0; i < 5; i++)
	{
		arr[i] += d[i];
		cout << arr[i] << " ";
	}
	memset(d, 0, sizeof(d));
	return 0;
}

输出:

-2 2 11 12 7

10.dfs全排列(n选m)

引例

例:输入一个数n,输出n的全排列(假如有编号为1,2,3的3张扑克牌和编号为1,2,3的3个盒子。将这3张扑克牌分别放入3个盒子一共有几种不同的放法呢?)

基本模型:

void dfs(int step){
	判断边界
	尝试每一种可能 for(i=1;i<=n;i++){
		继续下一步 dfs(step+1)
		}
	返回
}

代码如下:

#include<stdio.h>
int a[10],book[10],n;
//这里还有需要注意的地方C语言全局变量默认为0

void  dfs(int step){ //此时在第step盒子面前,需要往里面放第i张扑克牌
	int i;
	if(step==n+1){    //这里说明前面的n个盒子已经放好了,这是dfs结束的标志 
		for(i=1;i<=n;i++)
			printf("%d",a[i]);
		printf("\n");
		
		return ;
		/* 
		注意这个 return 它的作用不是返回主函数,而是返回上一级的dfs函数
		
		例:如果此时是  dfs(5),遇到这个 return 就会回到上一级的 dfs函数 
		也就是dfs(4),但此时dfs(4)的大部分语句已经执行了,只需要接着执行 book[i]=0
		然后继续进入for循环进入下一次的 dfs函数,直到结束。 		
		*/ 
		
	}
	 for(int i=1;i<=n;i++){
		if(book[i]==0){  //说明i号扑克牌还在手里,需要放入step号盒子
			a[step]=i;//将i号扑克牌放到第step个盒子中
			book[i]=1;//此时i号扑克牌已经被使用		
			dfs(step+1);
			/*注意这里是自己调用自己,表示此时走到了第step+1个盒子面前*/		
			book[i]=0;
			/*book[i]=0表示dfs调用结束了,换句话说就是扑克牌已经全部放完了
			  需要按照顺序将扑克牌收回,重新放,也就是前面所说的
			 */
		}
	}
	return;//这里表示这一级别的dfs函数已经结束了,返回上一级 dfs函数 

}
int main(){
	scanf("%d",&n);
	dfs(1);   //dfs函数的开始 
	return 0;
}

C++代码如下:

#include<iostream>
using namespace std;
int a[10],book[10],n;

void dfs(int step)
{
    int i;
    if(step==n+1)
    {
        for(i=1;i<=n;i++)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl;
        return ;
    }
    for(int i=1;i<=n;i++)
    {
        if(book[i]==0)
        {
            a[step]=i;
            book[i]=1;
            dfs(step+1);
            book[i]=0;
        }
    }
    return ;
}
int main()
{
    cin>>n;
    dfs(1);
    return 0;
}
牛客竞赛[NOIP2002]选数:https://ac.nowcoder.com/acm/problem/16706
题目描述

已知 n 个整数 x1,x2,…,xn,以及一个整数 k(k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4 个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为: 3+7+12=22  3+7+19=29  7+12+19=38  3+12+19=34。 现在,要求你计算出和为素数共有多少种。 例如上例,只有一种的和为素数:3+7+19=29)。

输入描述

输入格式为:n , k(1<=n<=20,k<n) x1,x2,…,xn (1<=xi<=5000000)

输出描述

输出格式为:一个整数(满足条件的种数)。

代码如下
#include<iostream>
using namespace std;
int n,k,arr[25],ans=0;
bool prime(int x)								//素数判断
{
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)return 0;
    }
    return 1;
}
void dfs(int m,int sum,int xu)					//m为选的个数,sum为选数的和
{
    if(m==k)								//如果选够了k个数,那么进行判断
    {
        if(prime(sum))ans++;					//若判断为素数,则ans++
        return ;
    }
    for(int i=xu;i<n;i++)dfs(m+1,sum+arr[i],i+1);	//xu的用处是保证不降原则,后面进行深搜
    return ;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)cin>>arr[i];
    dfs(0,0,0);
    cout<<ans;
}

11.floyd最短路

最短路		单源最短路	所有权都是正数	朴素Dijkstra算法	O(n^2)
								  堆优化版Dijkstra算法	O(mlogn)
					 存在负权边	Bellman-Ford	O(nm)
					 		    SPFA	一般O(m),最坏O(nm)
           多源汇最短路	Floyd算法	O(n^3)
AcWing 854.Floyd求最短路:https://www.acwing.com/problem/content/856/
题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,边权可能为负数。

再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出 impossible

数据保证图中不存在负权回路。

输入格式

第一行包含三个整数 n,m,k。

接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

接下来 k 行,每行包含两个整数 x,y,表示询问点 x 到点 y 的最短距离。

输出格式

共 k 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible

数据范围

1≤n≤200,1≤k≤n2,1≤m≤20000, 图中涉及边长绝对值均不超过 10000。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=210,INF=1e9;
int n,m,Q;
int d[N][N];
void floyd()
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int main()
{
    cin>>n>>m>>Q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)d[i][j]=0;
        	else d[i][j]=INF; 
    while(m--)
    {
        int a,b,w;
        cin>>a>>b>>w;
        d[a][b]=min(d[a][b],w);
    }
    floyd();
    while(Q--)
    {
        int a,b;
        cin>>a>>b;
        if(d[a][b]>INF/2)puts("impossible");
        else cout<<d[a][b]<<endl;
    }
}

12.邻接表+dijkstra最短路

AcWing 849. Dijkstra求最短路I :https://www.acwing.com/problem/content/851/
题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出 1号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1。

数据范围

1≤n≤500,1≤m≤105,图中涉及边长均不超过10000。

解题思路

1.初始化距离:dist[1]=0,dist[i]=+∞

2.迭代:for循环,i从0~n; s:当前以确定最短距离的点

找到不在s中的距离最近的点——>t

把t加到s里面

用t更新其他点的距离

稠密图:邻接矩阵 稀疏图:邻接表

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    for(int i=0;i<n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]>dist[j]))
                t=j;
        st[t]=true;
        for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+g[t][j]);
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=min(g[a][b],c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}

13.邻接表+堆优化dijkstra

AcWing 850. Dijkstra求最短路II:https://www.acwing.com/problem/content/852/
题目描述

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1。

数据范围

1≤n,m≤1.5×105,图中涉及边长均不小于 0,且不超过 10000。数据保证:如果最短路存在,则最短路的长度不超过 109

解题思路

1.初始化距离:dist[1]=0,dist[i]=+∞

2.迭代:for循环,i从0~n; s:当前以确定最短距离的点

找到不在s中的距离最近的点——>t n2 n

把t加到s里面 O(1)总共n次 n

用t更新其他点的距离 m次(m是边的数量) mlogn

稠密图:邻接矩阵 稀疏图:邻接表

堆	手写堆	n个数
	 优先队列	m个  mlogn
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N=150010;
int n,m;
int h[N],w[N],e[N],ne[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    priority_queue<PII,vector<PII>,greater<PII>>heap;
    heap.push({0,1});
    while(heap.size())
    {
        PII t=heap.top();
        heap.pop();
        int ver=t.second,distance=t.first;
        if(st[ver]) continue;
        st[ver]=true;
        
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>distance+w[i])
            {
                dist[j]=distance+w[i];
                heap.push({dist[j],j});
            }
        }
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=dijkstra();
    cout<<t<<endl;
}

14.01背包

AcWing 2. 01背包问题:https://www.acwing.com/problem/content/2/
题目描述

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000,0<vi,wi≤1000

解题思路
f[i][j]表示只看前i个物品,总体积是j的情况下,总价值最大是多少
result=max{f[n][0~v]}
f[i][j]:
1.不选第i个物品,f[i][j]=f[i-1][j];
2.选第i个物品,f[i][j]=f[i-1][j-v[i]];
f[i][j]=max{1. 2.}
f[0][0]=0;
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int n,m;
int f[N][N];
int v[N],w[N];

int main()
{
    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=0;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            if(j>=v[i])
                f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
        }
    int res=0;
    for(int i=0;i<=m;i++)res=max(res,f[n][i]);
    cout<<res<<endl;
}

15.高精度加法

AcWing 791.高精度加法:https://www.acwing.com/problem/content/793/
题目描述

给定两个正整数(不含前导 0),计算它们的和。

输入格式

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

输出格式

共一行,包含所求的和。

数据范围

1≤整数长度≤100000

#include<iostream>
#include<vector>
using namespace std;
const int N= 1e6+10;
//C=A+B
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;
    vector<int> A,B;
    
    cin>>a>>b;	//a="123456"
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');//A={6,5,4,3,2,1}
    for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
    
    auto C=add(A,B);
    for(int i=C.size()-1;i>=0;i--)cout<<C[i];
    return 0;
}

16.高精度乘法

AcWing 793.高精度乘法:https://www.acwing.com/problem/content/795/
题目描述

给定两个非负整数(不含前导 0) A 和 B,请你计算 A×B 的值。

输入格式

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

输出格式

共一行,包含 A×B 的值。

数据范围

1≤A的长度≤100000,0≤B≤10000

#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--)cout<<C[i];
    return 0;
}

17.BF字符串匹配

题目描述

给定主串S,判断模式串F是否是S的子串,如果是则返回T在S中出现的第一个位置,否则返回0。

算法思想

使用暴力算法解题,从主串S的第一个字符开始和模式串T的第一个字符进行比较,若相等则比较二者后续字符,否则,主串回溯到第二个字符,模式串回溯到第一个字符;继续比较,不匹配,主串回溯到第三个字符,模式串回溯到第一个字符。重新上述过程,直到模式串T中的字符全部比较完毕,说明匹配成功;否则匹配失败。

#include <iostream>
using namespace std;
int BF(char *S,char *T){
    int i=0,j=0;
    while(S[i]!='\0'&&T[j])
    {
        if(S[i]==T[j])
        {
            i++;
            j++;
        }
        else
        {
            i=i-j+1;//主串回溯到上次回溯位置的下一个位置
            j=0;//模式串回溯到第一个位置
        }
    }
    if(T[j]=='\0')
        return i-j+1; //返回T在S中第一次出现的位置
    else return 0;
}

int main(){

    char S[50]={'a','b','a','c','b','a','b','c'};
    char T[50]={'b','a','b','c'};
    cout<<BF(S,T)<<"\n";
    return 0;
}

18.BFS迷宫问题

AcWing 844.走迷宫 :https://www.acwing.com/problem/content/846/
题目描述

给定一个 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

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N=110;
int n,m;
int g[N][N];
int d[N][N];
PII q[N*N];
int bfs()
{
    int hh=0,tt=0;
    q[0]={0,0};
    memset(d,-1,sizeof d);
    d[0][0]=0;
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    while(hh<=tt)
    {
        auto t=q[hh++];
        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&&d[x][y]==-1)
            {
                d[x][y]=d[t.first][t.second]+1;
                q[++tt]={x,y};
            }
        }
    }
    return d[n-1][m-1];
}
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;
}

19.Kruskal求最小生成树

AcWing 859. Kruskal算法求最小生成树:https://www.acwing.com/problem/content/description/861/
题目描述

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。

由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式

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

接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1≤n≤105,1≤m≤2∗105,图中涉及边的边权的绝对值均不超过 1000。

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

const int N = 100010, M = 200010;
int n,m,p[N];

struct EDGE
{
    int a,b,w;
}edge[M];

//结构体排序我还是更习惯这样,清晰,小的在前面;
bool cmp(EDGE a,EDGE b)
{
    return  a.w<b.w;
}

//并查集,正好在图论这儿复习了,p[x]里面存的是x的父亲,如果p[x]的父亲就是他自己,那说明他是祖宗
int find(int x)  // 并查集。find的功能是找父亲。
{
    if (p[x] != x) p[x] = find(p[x]);//不是祖宗,就找父亲的父亲。
    return p[x];
}


int main()
{
    cin>>n>>m;

    for(int i= 0;i<m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;

        //这样表示明显比这样方便(
        //edge[i].a=x,edge[i].b=y,edge[i].w=z;
        edge[i]={x,y,z};

    }

    //结构体排序,自己写一下cmp
    sort(edge,edge+m,cmp);

    //打表找bug时用的,忽略。for(int i =0 ; i< m;i++) printf("%d\n",edge[i].w);

    //万物伊始,每个人都是祖宗
    for(int i =0;i<n;i++) p[i]=i;

    int res=0,cnt=0;//非全局变量使用前看来要初始化。res是权值,cnt是边数

    //kruskal算法:从最小的边开始画,直到把整个图画通。
    for(int i =0;i<m;i++)
    {
        int a =edge[i].a,b=edge[i].b,w=edge[i].w;
        a=find(a),b=find(b);
        if(a!=b)
        {
            p[a]=b;
            res+=w;
            cnt++;
        }
    }

    //连接n个点至少需要n-1条边,如果不够,那肯定是不连通
    if(cnt<n-1) puts("impossible");
    else printf("%d",res);
}

20.Prim求最小生成树

AcWing 858. Prim算法求最小生成树:https://www.acwing.com/activity/content/problem/content/924/
题目描述

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|,m=|E|。

由 V 中的全部 n 个顶点和 E 中 n−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式

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

接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围

1≤n≤500,1≤m≤105, 图中涉及边的边权的绝对值均不超过 10000。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 510;
int g[N][N];//存储图
int dt[N];//存储各个节点到生成树的距离
int st[N];//节点是否被加入到生成树中
int pre[N];//节点的前去节点
int n, m;//n 个节点,m 条边

void prim()
{
    memset(dt,0x3f, sizeof(dt));//初始化距离数组为一个很大的数(10亿左右)
    int res= 0;
    dt[1] = 0;//从 1 号节点开始生成 
    for(int i = 0; i < n; i++)//每次循环选出一个点加入到生成树
    {
        int t = -1;
        for(int j = 1; j <= n; j++)//每个节点一次判断
        {
            if(!st[j] && (t == -1 || dt[j] < dt[t]))//如果没有在树中,且到树的距离最短,则选择该点
                t = j;
        }

        //2022.6.1 发现测试用例加强后,需要判断孤立点了
        //如果孤立点,直返输出不能,然后退出
        if(dt[t] == 0x3f3f3f3f) {
            cout << "impossible";
            return;
        }


        st[t] = 1;// 选择该点
        res += dt[t];
        for(int i = 1; i <= n; i++)//更新生成树外的点到生成树的距离
        {
            if(dt[i] > g[t][i] && !st[i])//从 t 到节点 i 的距离小于原来距离,则更新。
            {
                dt[i] = g[t][i];//更新距离
                pre[i] = t;//从 t 到 i 的距离更短,i 的前驱变为 t.
            }
        }
    }

    cout << res;

}

void getPath()//输出各个边
{
    for(int i = n; i > 1; i--)//n 个节点,所以有 n-1 条边。

    {
        cout << i <<" " << pre[i] << " "<< endl;// i 是节点编号,pre[i] 是 i 节点的前驱节点。他们构成一条边。
    }
}

int main()
{
    memset(g, 0x3f, sizeof(g));//各个点之间的距离初始化成很大的数
    cin >> n >> m;//输入节点数和边数
    while(m --)
    {
        int a, b, w;
        cin >> a >> b >> w;//输出边的两个顶点和权重
        g[a][b] = g[b][a] = min(g[a][b],w);//存储权重
    }

    prim();//求最下生成树
    //getPath();//输出路径
    return 0;
}

标签:输出,int,sum,namespace,ACM,C++,整数,include,模板
From: https://blog.51cto.com/u_16343220/8194111

相关文章

  • C++语法——noexcept 关键字
    noexcept问题在数据库项目CMU15445中的Project#2中,有以下一个构造函数的实现:BasicPageGuard(BasicPageGuard&&that)noexcept;这里为什么选择加noexcept?解释关键字noexcept在C++中用来指定一个函数不会抛出异常。在函数声明后使用noexcept表明该函数保证不会......
  • C++11语法——std::move()
    std::move()在C++中,std::move()用于将对象转换为右值引用。关于左值、左值引用、右值、右值引用左值是一个表示数据的表达式(比如变量名或者解引用的指针),程序可以获取其地址传统的C++引用,即是左值引用。C++11新增右值引用,用&&表示。右值是可出现在赋值表达式的右边,但不......
  • SATA基础+更改终端颜色+PCI.ids位置+Linux和Windows的scanf+C语言C++的局部变量与全局
    SATA基础https://zhuanlan.zhihu.com/p/554251608物理信号物理层功能时钟恢复:对于高频传输,一般是采用差分信号传输,并且没有单独的时钟,时钟存在于编码内部串并转换:对于高频传输,串联信号可以做到更高的频率。字节对其:8/10编码转换的10bit对其链路层、传输层链路层和传输......
  • 漫谈 C++:良好的编程习惯与编程要点
    以良好的方式编写C++class假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯。①Header(头文件)中的防卫式声明complex.h:#ifndef__COMPLEX__#define__COMPLEX__classcomplex{}#endif防止头文件的内容被多次包含。②把数据放在priva......
  • DFS、BFS模板
    目录DFSBFSDFS处理当前节点的位置不同对应着不同的遍历defpreorderTraversal(root):ifnotroot:returnprint(root.val)#前序遍历,处理当前节点preorderTraversal(root.left)#递归遍历左子树print(root.val)#中序遍历,处理当前节点pr......
  • C++对象模型
    思考:对于实现平面一个点的参数化。C++的class封装看起来比C的struct更加的复杂,是否意味着产生更多的开销呢?实际上并没有,类的封装不会产生额外的开销,其实,C++中在布局以及存取上的额外开销是virtual引起的。C++对象模式在C++中,有两种classdatamembers:静态成员和非静态成员。有......
  • c++实现排序算法
    排序算法选择排序#include<iostream>#include<cmath>usingnamespacestd;intmain(){ intn,i,j,a[2000]; boolt; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<n;i++) for(j=i+1;j<=n;j++) if......
  • Windows系统 C/C++程序编译后首次执行时间很长 断网则正常执行 的解决方法
    Windows系统C/C++程序编译后首次执行时间很长断网则正常执行的解决方法问题描述运行环境:Win10、Win11或其他Win环境。在各类IDE(包括但不限于VC6/VisualStuido等)编译任意C/C++源码(无论该程序有多简单),首次运行时间异常地长,即在黑窗口无任何输出。等待一段时间后有程序正......
  • C++prime之输入输出文件
    作为一种优秀的语言,C++必然是能操作文件的,但是我们要知道,C++是不直接处理输入输出的,而是通过一族定义在标准库中的类型来处理IO的。‘流’和‘缓冲区’‘流’和‘缓冲区’C++程序把输入输出看作字节流,并且其只检查字节流,不需知道字节来自何方。管理输入包括两步:将流与输入去......
  • 与c++比较学习rust3-2:数据类型
    rust的文章在数据类型数据类型标量类型整形,浮点型,布尔型,字符整形c++rustgoint8_ti8int8int16_ti16int16int32_ti32int32int64_ti64int64-i128-intisizeintunsignedintusizeuintuint8_tu8uint8uint16_tu16uint16ui......