备战蓝桥杯算法整合

整合这一段时间备战蓝桥杯学习的算法,方便复习!!向国一冲刺

算法目录

  • 整合这一段时间备战蓝桥杯学习的算法,方便复习!!向国一冲刺
    • 六倍法判断素数
    • 欧拉筛
    • 01背包
    • 完全背包
    • 多重度背包
    • Floyd-Warshall(多源最短路)
    • Dijkstra(单源最短路)
    • Bellman-Ford最短路算法
    • 最大公约数
    • 最小公倍数
    • 分解质因数
    • 全排列(递归)
    • 拓扑排序
    • 并查集
    • 二分算法
    • 二分答案
    • 尺取法
    • 折半枚举
    • 线段树
    • 线段树乘加法混合
    • 高精度加法
    • 高精度减法
    • 高精度乘法
    • Kruskal算法(最小生成树)
    • BFS
    • DFS

六倍法判断素数

bool is_sprime(int x){
	if(x==1)return false;//1不是素数 
	if(x==2||x==3||x==5){//2,3,5是素数 
		return true;
	}
	if(x%2==0||x%3==0){//2和3的倍数一定不是素数 
		return false;
	}
	for(int i=5;i<=sqrt(x);i+=6){
		if(x%i==0||x%(i+2)==0){
			return false;
		}
	}
	return true;
}

传送门

欧拉筛

bool check[10000];//标记合数
vector<int> prime;
void isprime(int n){
	check[0]=true;
	check[1]=true;
	for(int i=2;i<=n;i++){
		if(!check[i])prime.push_back(i);
		for(int j=0;j<prime.size()&&i*prime[j]<=n;j++){
			check[i*prime[j]]=true;
			if(i%prime[j]==0)break;
		}
	}
}

传送门

01背包

for(int i=1;i<=n;i++){//n是物品总数 
		for(int j=m;j>=w[i];j--){//m是背包大小 
			dp[j]=max(dp[j],v[i]+dp[j-w[i]]);
		}
	}

传送门

完全背包

	for(int i=1;i<=n;i++){
		for(int j=w[i];j<=m;j++){
			dp[j]=max(dp[j],v[i]+dp[j-w[i]]);
		}
	}

传送门

多重度背包

struct node{//包结构体 
	int v,w;//价值重量 
	node(int vv,int ww){
		v=vv;
		w=ww;
	}
};
vector<node> goods;

for(int i=1;i<=n;i++){//拆包 
	int tv,tw,s;
	cin>>tv>>tw>>s;
	for(int k=1;k<=s;k*=2){//二进制优化 
		s-=k;
		goods.push_back(node(tv*k,tw*k));//装包 
	}
	if(s>0)goods.push_back(node(tv*s,tw*s));//剩下的也要装入包中 
}
	//01背包
for(int i=0;i<goods.size();i++){
	for(int j=m;j>=goods[i].w;j--){
		dp[j]=max(dp[j],goods[i].v+dp[j-goods[i].w]);
	}
} 

传送门

Floyd-Warshall(多源最短路)

	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
			}
		}
	}

传送门

Dijkstra(单源最短路)

无法处理带有负权边和负权回路的图

book[cnt]=true;//起始点标记为访问过
for(int i=1;i<=n-1;i++){//需要松弛n-1次
		int mint=INF;
		for(int j=1;j<=n;j++){//找出和起始点最近的点 
			if(!book[j]&&dis[j]<mint){
				u=j;//记录最近点下标
				mint=dis[j];//记录最近点距离
			}
		}
		book[u]=true;//标记为访问过
		for(int v=1;v<=n;v++){//查询和最近点有连接的点 
			if(e[u][v]<INF){//保证u可以到v 
				dis[v]=min(dis[v],dis[u]+e[u][v]);//松弛 
			}
		}
	}

传送门
堆优化:

/*
测试数据
输入:
6 9 1
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
输出:
0 1 8 4 13 17
*/ 
#include
#include
#include
#include
using namespace std;
const int INF = 2147483647;
int n,m,start,dis[10005],vis[10005];
struct node{
	int v,w;
	node(int vv,int ww){
		v=vv;
		w=ww;
	}
};
vector<node> ve[10005];//邻接表存图 
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > que;//小顶堆 
int main(){
	//输入 
	cin>>n>>m>>start;
	for(int i=0;i<m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		ve[x].push_back(node(y,z));//建立邻接表 
	}
	//初始化dis
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	} 
	dis[start]=0;
	//初始化队列
	que.push(make_pair(0,start));
	//dijkstra算法 
	while(!que.empty()){
		int u = que.top().second;
		que.pop();
		if(vis[u])continue;//这个点松弛过 
		vis[u]=true;
		for(int i=0;i<ve[u].size();i++){//寻找和u相邻的点 
			int v = ve[u][i].v;
			int w = ve[u][i].w;//距离 
			if(dis[v]>dis[u]+w){//判断是否可以松弛 
				dis[v]=dis[u]+w;//松弛 
				que.push(make_pair(dis[v],v));//入队 
			}
		}
		
	} 
	for(int i=1;i<=n;i++){
		printf("%d ",dis[i]);
	}
	return 0;
} 

Bellman-Ford最短路算法

#include
#include
using namespace std;
const int INF = 2147483647;
int n,m,cnt,u[500100],v[500100],w[500100];
long long dis[10100];//dis数组需要开到long long 
int main(){
	//输入 
	cin>>n>>m>>cnt;
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i]>>w[i];
	}
	//初始化 
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	}
	dis[cnt]=0;
	//Bellman-Ford核心语句 
	for(int k=1;k<=n-1;k++){
		bool check=false;//判断本轮松弛最短路有没有变化 
		for(int i=1;i<=m;i++){
			if(dis[v[i]]>dis[u[i]]+w[i]){//不能直接使用min函数 
				dis[v[i]]=dis[u[i]]+w[i];
				check=true;//dis数组发生更新,改变check的值	
			} 
		}
		if(check==false)break;//没有变化就没必要继续松弛了,提前结束 
	} 
	for(int i=1;i<=n;i++){
		printf("%d ",dis[i]);
	}
	return 0;
} 

检测负环回路:

//Bellman-Ford核心语句 
	for(int k=1;k<=n-1;k++){
		for(int i=1;i<=m;i++){
			dis[v[i]]=min(dis[v[i]],dis[u[i]]+w[i]);	
		}
	} 
	bool flag=false;
	for(int i=1;i<=m;i++){//n-1次松弛之后还可以松弛
		if(dis[v[i]]>dis[u[i]]+w[i])flag=true;
	} 
	if(flag)printf("此图有负权回路\n");

队列优化:

/*
5 5 1
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
输出:
0 -3 -1 2 4 
*/ 
#include
#include
#include
#include
using namespace std;
const int INF = 2147483647;
int n,m,start,dis[10005],vis[10005];//vis标记是否在队列中 
struct node{
	int v,w;
	node(int vv,int ww){
		v=vv;
		w=ww;
	}
};
vector<node> ve[10005];
queue<int> que;
int main(){
	//输入 
	cin>>n>>m>>start;
	for(int i=0;i<m;i++){
		int x,y,z;
		cin>>x>>y>>z;
		ve[x].push_back(node(y,z));//建立邻接表 
	}
	//初始化dis
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	}
	 
	dis[start]=0;
	//初始化队列
	que.push(start);
	vis[start]=true;
	//Bellman算法堆优化 
	while(!que.empty()){
		int u = que.front();
		//出队 
		que.pop();
		vis[u]=false;
		for(int i=0;i<ve[u].size();i++){
			int v = ve[u][i].v;
			int w = ve[u][i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				//不在队列中 
				if(!vis[v]){
					que.push(v);
				}
			}
		}
	} 
	for(int i=1;i<=n;i++){
		printf("%d ",dis[i]);
	}
	return 0;
} 

队列优化检测负环:如果一个顶点进入队列超过n次就肯定存在负环
传送门

最大公约数

int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

传送门

最小公倍数

int icm(int a,int b){
	return a*b/gcd(a,b);
}

传送门

分解质因数

void zhiyinshu(int n){
	is_prime(n);//利用欧拉筛建素数表 
	int i=0;
	while(n!=1){//n==1找完了所有质因数
		if(!n%prime[i]){
			printf("%d\n",prime[i]);//找到了一个质因数
			n/=prime[i]; 
			continue;//质因数可以相同 
		} 
		i++;//遍历下一个素数 
		
	}
}

传送门

全排列(递归)

void permutaion(int k){//调用permutation(0) 
	if(k==n){//完成一次排列 
		if(check()){//判断是否满足条件 
			//操作 
		}
		return;
	}
	for(int i=k;i<n;i++){
		swap(a[i],a[k]);
		permutaion(k+1);
		swap(a[i],a[k]);
	}
}

区间全排列

void permutation(int p,int q){//对区间[p,q]进行全排列 
	if(p==q){//出口 
		if(check()){//用来检查题目的条件 
		     //执行操作 
		}
		return; 
	}
	for(int i=p;i<q;i++){
		swap(a[i],a[p]); 
		permutation(p+1,q);
		swap(a[i],a[p]);
	}
}

传送门

拓扑排序

#include
#include
#include
#include
using namespace std;
vector<int> edge[100000];//邻接链表
int degree[100000];
int n,m; 
void top(){
	queue<int> node;
	//找到入度为零的点 
	for(int i=1;i<=n;i++){
		if(degree[i]==0){
			node.push(i);
		}
	}
	while(!node.empty()){
		int now = node.front();//输出序列
		printf("%d ",now);
		node.pop();
		for(int i=0;i<edge[now].size();i++){//找相邻节点 
			int dot = edge[now][i];
			degree[dot]--;
			if(degree[dot]==0){//入度为零入队 
				node.push(dot);
			} 
		}
	}
} 
int main(){
	cin>>n>>m;
	//建立邻接链表 
	for(int i=0;i<=m;i++){
		int x,y;
		cin>>x>>y;
		if(x==0&&y==0)break;
		degree[y]++;
		edge[x].push_back(y);
	}
	top();
	return 0;
}

传送门

并查集

//初始化
for(int i=1;i<=n;i++){
		parent[i]=i;//所有的点祖宗是自己
		rank[i]=0;
}


//找根节点(找祖宗) 
int find(int x){
	if(x==parent[x])return x;//自己的爸爸是自己,自己就是祖宗 
	return parent[x]=find(parent[x]);//路径压缩,将经过的点的祖宗全部更新 
}


//按秩合并集合 
void unite(int x,int y){
	int x_root=find(x);
	int y_root=find(y);
	if(x_root==y_root)return;//在同一集合中
	//认层数多的树的根节点做祖宗 
	if(rank[x_root]>rank[y_root]){
		parent[y_root]=x_root;
	}else{
		parent[x_root]=y_root;
		if(rank[x_root]==rank[y_root]){
			rank[y_root]++;//当两棵树一样高时,认y_root树做祖宗会是整体层数加一 
		}
	}
}

传送门

二分算法

//返回第一个大于或者等于x的的数的下标 
int lower_bound(int x,int n,int a[]){
	int lb = -1,ub = n;
	while(ub-lb>1){
		int mid=(ub+lb)/2;
		if(x<=a[mid]){
			ub=mid;
		}else{
			lb=mid;
		}
	}
	return ub;
}
//返回最后一个小于或者等于x的数的下标 
int upper_bound(int x,int n,int a[]){
	int lb=-1,ub=n;
	while(ub-lb>1){
		int mid=(ub+lb)/2;
		if(x>=a[mid]){
			lb=mid;
		}else{
			ub=mid;
		}
	}
	return lb;
} 

传送门

二分答案

存在一个分界点,小于分界点的不合法,大于分界点的不如他优

//求最大值情况 
int solve(){
	int lb=0,ub=INF;//可根据实际情况确定答案区间
	for(int i=0;i<100;i++){
		int mid = (lb+ub)/2;//有的情况是(lb+ub+1)/2; 
		if(check(mid)){//判断答案是mid使是否满足题目要求 
			lb=mid;//舍弃区间[lb,mid-1] 
		}else{
			ub=mid-1;//舍弃区间[mid,ub] 
		}
	} 
	return ub;//返回最大值 
}
//求最小值情况
int solve(){
	int lb=0,ub=INF;//根据实际情况确定答案区间
	for(int i=0;i<100;i++){
		int mid = (ub+lb)/2;//有的情况是(ub+lb+1)/2;
		if(check(mid)){
			ub=mid;//舍弃区间[mid+1,ub] 
		}else{
			lb=mid+1;//舍弃区间[lb,mid] 
		}
	} 
	return lb;
}

传送门

尺取法

滑动窗口原理,涉及到元素种类问题结合map一起会更方便

还有关于连续递增M的个元素最大区间和问题,可以先将递增区间拓展到最大,然后逐渐左指针右移缩小区间,当区间长度满足条件时就是这一段递增区间的最优解,然后进入下一个递增区间同样处理即可

int i=0,j=0,sum=a[0];//初始化指针和区间和
	while(i<=j&&j<n){
		if(check()){//如果这段区间满足条件 
			//做相应操作 
			sum-=a[i++];//减去左指针指向的数,并且左指针后移,减小区间长度 
		}else{
			sum+=a[++j];//右指针后移,并且加上新数,增大区间长度 
		}
	} 

传送门

折半枚举

传送门

线段树

//节点用结构体保存 
struct Node{
	int lazy,sum;
};
Node tree[10000];
//懒标记下放 
void Push_Down(int node,int len){
	if(!tree[node].lazy){//存在懒标记 
		tree[node<<1].lazy+=tree[node].lazy;//懒标记下推 
		tree[node<<1|1].lazy+=tree[node].lazy;//懒标记下推 
		tree[node<<1].sum+=(len-(len>>1))*tree[node].lazy;//更新左儿子的值 
		tree[node<<1|1].sum+=(len>>1)*tree[node].lazy;//更新右儿子的值 
	}
}
//建树 
void build_tree(int node,int L,int R){
	tree[node].lazy=0;//初始化lazy 
	if(L==R){//找到了叶子节点 
		tree[node].sum=arr[L];
		return;
	}
	int mid = (L+R)>>1;
	build_tree(node<<1,L,mid);
	build_tree(node<<1|1,mid+1,R);
	tree[node].sum=tree[node<<1].sum+tree[node<<1|1].sum;//更新父节点 
}
//更新树 
void update_tree(int node,int L,int R,int x,int y,int num){//将区间[x,y]所有元素执行操作 
	if(x<=L&&R<=y){//在需要更新的区间内
		tree[node].lazy+=num;
		tree[node].sum+=(R-L+1)*num;
		return; 
	}
	int mid = (L+R)>>1;
	if(x<=mid){//更新区间和左子树有交集 
		update_tree(node<<1,L,mid,x,y,num);
	}
	if(y>=mid+1){//更新区间和右子树有交集 
		update_tree(node<<1|1,mid+1,R,x,y,num);
	}
	tree[node].sum=tree[node<<1].sum+tree[node<<1|1].sum;
	
}
//查询树
int find_tree(int node,int L,int R,int x,int y){
	if(x<=L&&R<=y){
		return tree[node].sum;
	}
	int mid = (L+R)>>1;
	long long ans=0;
	if(x<=mid){//查询区间和左子树有交集 
		ans+=find_tree(node<<1,L,mid,x,y);
	}
	if(y>=mid+1){//查询区间和右子树有交集 
		ans+=find_tree(node<<1|1,mid+1,R,x,y);
	}
	return ans;
} 

线段树乘加法混合

#include
#include
#define left node*2
#define right node*2+1//左孩子 
#define ll long long//右孩子 
using namespace std;
const int N = 100007;
ll a[N],p;
void prin();
int n,m;
struct node{
	ll muti,add,sum;
}tree[N<<2];//节点数最多不超过N的四倍 
//上推
void push_up(int node){
	tree[node].sum=tree[left].sum+tree[right].sum;
	tree[node].sum%=p;
} 
//下推
void push_down(int node,int len){
	int leftlen=len-(len>>1);//做孩子区间大小 
	int rightlen=len>>1;//右孩子区间的大小 
	//下推和(注意:先乘法在加法)
	tree[left].sum=tree[left].sum*tree[node].muti+tree[node].add*leftlen;
	tree[left].sum%=p; 
	tree[right].sum=tree[right].sum*tree[node].muti+tree[node].add*rightlen;
	tree[right].sum%=p;
	//下推乘标记
	tree[left].muti=(tree[left].muti*tree[node].muti)%p;
	tree[right].muti=(tree[right].muti*tree[node].muti)%p;
	//下推加标记(标记也要先乘后加)
	tree[left].add= (tree[left].add*tree[node].muti+tree[node].add)%p;
	tree[right].add = (tree[right].add*tree[node].muti+tree[node].add)%p;
	//清空标记
	tree[node].add=0;
	tree[node].muti=1; 
} 
//建树 
void build_tree(int node,int L,int R){
	//初始化 
	tree[node].add=0;
	tree[node].muti=1;
	tree[node].sum=0;
	if(L==R){
		tree[node].sum=a[L]%p;//填入叶子节点 
		return;
	}
	int mid = (L+R)>>1;
	build_tree(left,L,mid);//递归左子树 
	build_tree(right,mid+1,R);//递归右子树 
	push_up(node);//上推给父亲节点 
	tree[node].sum%=p;
	return; 
}
//更新乘法
void muti_update(int node,int L,int R,int x,int y,ll k){
	if(y<L||x>R)return;//出界 
	if(x<=L&&R<=y){//[L,R]区间需要更新 
		tree[node].sum=(tree[node].sum*k)%p;//更新和 
		tree[node].muti=(tree[node].muti*k)%p;//更新乘法标记 
		tree[node].add=(tree[node].add*k)%p;//一旦执行乘法,加法标记也需要乘一遍(先乘后加原则) 
		return; 
	}
	push_down(node,R-L+1);//下放标记 
	int mid = (L+R)>>1;
	muti_update(left,L,mid,x,y,k);//递归左树 
	muti_update(right,mid+1,R,x,y,k);//递归右树 
	push_up(node);//更新父节点值 
	tree[node].sum%=p;
	return;
} 
//更新加法
void add_update(int node,int L,int R,int x,int y,int num){
	if(y<L||x>R)return;//出界 
	if(x<=L&&R<=y){
		tree[node].sum=(tree[node].sum+(R-L+1)*num)%p;//直接加就可以了,因为如果执行过乘法,已经提前处理过 
		tree[node].add=(tree[node].add+num)%p;//加法标记一样 
		return;
	}
	push_down(node,R-L+1);//下放标记 
	int mid = (L+R)>>1;
	add_update(left,L,mid,x,y,num);//递归左子树 
	add_update(right,mid+1,R,x,y,num);//递归右子树 
	push_up(node);//更新父亲节点 
	tree[node].sum%=p;
	return;
} 
//查询
ll find_tree(int node,int L,int R,int x,int y){
	if(y<L||x>R)return 0;//出界 
	if(x<=L&&R<=y){
		return tree[node].sum%p;
	}
	push_down(node,R-L+1);//下放 
	int mid = (L+R)>>1;
	return (find_tree(left,L,mid,x,y)+find_tree(right,mid+1,R,x,y))%p;//返回区间和注意取模 
} 
int main(){
    scanf("%d%d%d", &n, &m, &p);
    for(int i=1; i<=n; i++){
        scanf("%lld", &a[i]);
    }
    build_tree(1, 1, n);
    while(m--){
        int chk;
        scanf("%d", &chk);
        int x, y;
        long long k;
        if(chk==1){
            scanf("%d%d%lld", &x, &y, &k);
            muti_update(1, 1, n, x, y, k);//乘法 
        }
        else if(chk==2){
            scanf("%d%d%lld", &x, &y, &k);
            add_update(1, 1, n, x, y, k);//加法 
        }
        else{
            scanf("%d%d", &x, &y);
            printf("%lld\n", find_tree(1, 1, n, x, y));//查询 
        }
    }
    return 0;
}

版本二:

#include
#include
#define left node*2
#define right node*2+1
#define mid (L+R)/2
#define maxn 100007
#define ll long long
using namespace std;
ll arr[maxn];
ll p,n,m;
struct node{
	ll muti,add,sum;
}tree[maxn<<2];
void push_up(int node){
	tree[node].sum=tree[left].sum+tree[right].sum;
	tree[node].sum%=p;
}
void push_down(int node,int len){
	int leftlen = len-(len>>1);
	int rightlen = len>>1;
	//下推和
	tree[left].sum=tree[left].sum*tree[node].muti+leftlen*tree[node].add;
	tree[left].sum%=p;
	tree[right].sum=tree[right].sum*tree[node].muti+rightlen*tree[node].add;
	tree[right].sum%=p;

	//下推乘
	tree[right].muti=(tree[right].muti*tree[node].muti)%p;
	tree[left].muti=(tree[left].muti*tree[node].muti)%p;
	//下推加
	tree[left].add=tree[left].add*tree[node].muti+tree[node].add;
	tree[left].add%=p;
	tree[right].add=tree[right].add*tree[node].muti+tree[node].add; 
	tree[right].add%=p;
	//标志重置
	tree[node].add=0;
	tree[node].muti=1; 
}
//建树
void build_tree(int node,int L,int R){
	tree[node].add=0;
	tree[node].muti=1;
	tree[node].sum=0;
	if(L==R){
		tree[node].sum=arr[L]%p;
		return;
	}
	build_tree(left,L,mid);
	build_tree(right,mid+1,R);
	push_up(node);
} 
//乘更新树
void muti_update_tree(int node,int L,int R,int x,int y,int k){
	if(x<=L&&R<=y){
		tree[node].sum=(tree[node].sum*k)%p;
		tree[node].muti=(tree[node].muti*k)%p;
		tree[node].add=(tree[node].add*k)%p;
		return;
	}
	push_down(node,R-L+1);
	if(x<=mid){
		muti_update_tree(left,L,mid,x,y,k);
	}
	if(y>=mid+1){
		muti_update_tree(right,mid+1,R,x,y,k);
	}
	push_up(node);
} 
//加更新
void add_update_tree(int node,int L,int R,int x,int y,int num){
if(x<=L&&R<=y){
		tree[node].sum=(tree[node].sum+num*(R-L+1))%p;
		tree[node].add=(tree[node].add+num)%p;
		return;
	}
	push_down(node,R-L+1);
	if(x<=mid){
		add_update_tree(left,L,mid,x,y,num);
	}
	if(y>=mid+1){
		add_update_tree(right,mid+1,R,x,y,num);
	}
	push_up(node);
} 
//查询
long long find_tree(int node,int L,int R,int x,int y){
	if(x<=L&&R<=y){
		return tree[node].sum%p;
	}
	push_down(node,R-L+1);
	long long ret=0;
	if(x<=mid){
		ret+=find_tree(left,L,mid,x,y);
	}
	if(y>=mid+1){
		ret+=find_tree(right,mid+1,R,x,y);
	}
	return ret%p;
} 
int main(){
	scanf("%lld %lld %lld",&n,&m,&p);
	for(int i=1;i<=n;i++){
		scanf("%d",&arr[i]);
	}
	build_tree(1,1,n);
	while(m--){
		int t,x,y;
		scanf("%d %d %d",&t,&x,&y);
		ll k;
		if(t==1){
			scanf("%lld",&k);
			muti_update_tree(1,1,n,x,y,k);
		}else if(t==2){
			scanf("%lld",&k);
			add_update_tree(1,1,n,x,y,k);
		}else if(t==3){
			printf("%lld\n",find_tree(1,1,n,x,y)%p);
		}
	}
	
	return 0;
}

传送门

高精度加法

1.字符串输入
2.反向装入整型数组中
3.每位相加(注意进位)
4.反向输出
方法一

#include
#include
const int maxn = 100000005;
using namespace std;
int a[maxn],b[maxn],c[maxn];
int main(){
	//输入 
	string A,B;
	cin>>A>>B;
	//获取长度 
	int la = A.length();
	int lb = B.length(); 
	//反向装入整型数组 
	for(int i=0;i<la;i++){
		a[la-i]=A[i]-'0';
	} 
	for(int i=0;i<lb;i++){
		b[lb-i]=B[i]-'0';
	}
	int lc = max(la,lb);//运算结果值长度 
	int k=0;//进位
	//计算 
	for(int i=1;i<=lc;i++){
		c[i]=(a[i]+b[i]+k)%10;
		k = (a[i]+b[i]+k)/10;
	}
	//还剩进位 
	if(k>0){
		c[++lc]=k;//多进一位 
	} 
	//反向输出 
	for(int i=lc;i>=1;i--){
		cout<<c[i];
	}
	return 0;
	
} 

方法二

string add(string a,string b){
	//去前导零
	a=a.substr(a.find_first_not_of('0'));
	b=b.substr(b.find_first_not_of('0'));
	 
	int lenA=a.length();
	int lenB=b.length();
	//反转从低位往高位计算 
	reverse(a.begin(),a.end());
	reverse(b.begin(),b.end());
	
	
	int len = max(lenA,lenB)+10;//便于最高位进位 
	string ans(len,'0');//初始化一定长度ans字符串,便于计算 
	//将a存入c中 
	for(int i=0;i<lenA;i++){
		ans[i]=a[i];
	} 
	
	int tmp=0;//保存进位 
	//进行运算
	for(int i=0;i<len;i++){
		if(i<lenB){
			tmp+=(ans[i]-'0')+(b[i]-'0');//此时b需要参加运算 
		}else{
			tmp+=ans[i]-'0';//b不需要参加运算 
		}
		ans[i]=tmp%10+'0';
		tmp/=10;//进位 
	}
	
	//反转回来 
	reverse(ans.begin(),ans.end());
	//去前导零
	ans=ans.substr(ans.find_first_not_of('0'));
	
	return ans;
}

传送门

高精度减法

1.字符串输入
2.判断两数大小,保证大数在前,是负数首先输出符号
3.反向装入整型数组中
4.每位相减(注意借位)
5.去除前导零(最少留一个0)
6.反向输出

#include
#include
using namespace std;
const int maxn = 10000005;
int a[maxn],b[maxn],c[maxn];
int main(){
	//输入 
	string A,B;
	cin>>A>>B;
	//获取长度 
	int la = A.length();
	int lb = B.length();
	//判断结果正负
	if(la<lb||(la==lb&&A<B)){
		cout<<'-';
		swap(A,B);
		swap(la,lb); 
	}
	 
	//反向装入整型数组
	for(int i=0;i<la;i++){
		a[la-i]=A[i]-'0';
	} 
	for(int i=0;i<lb;i++){
		b[lb-i]=B[i]-'0';
	}
	
	int lc = la;//最终结果值长度
	//计算 
	for(int i=1;i<=lc;i++){
		if(a[i]<b[i]){//需要借位 
			a[i]+=10;
			a[i+1]--;
		}
		c[i]=a[i]-b[i];
	} 
	//去前导零,当A==B时保留一个零
	while(c[lc]==0&&lc>1)lc--;
	//反向输出 
	for(int i=lc;i>=1;i--){
		cout<<c[i];
	} 
	
	return 0;
}

传送门

高精度乘法

1.字符串输入
2.反向装入整型数组中
3.交叉相乘(双重循环)
5.去除前导零(最少留一个0)
4.反向输出

#include
#include
using namespace std;
const int maxn = 10000005;
int a[maxn],b[maxn],c[maxn];
int main(){
	//输入 
	string A,B;
	cin>>A>>B;
	int la = A.length();
	int lb = B.length();
	//反向装入整型数组
	for(int i=0;i<la;i++){
		a[la-i]=A[i]-'0';
	}	
	for(int i=0;i<lb;i++){
		b[lb-i]=B[i]-'0';
	}
	//计算,交叉相乘
	for(int i=1;i<=la;i++){
		for(int j=1;j<=lb;j++){
			c[i+j-1]+=a[i]*b[j];//i和j乘结果放在i+j-1个位置 
			c[i+j]+=c[i+j-1]/10;//进位 
			c[i+j-1]%=10;//保留余下的 
		}
	} 
	int lc = la+lb;
	//去前导零
	while(c[lc]==0&&lc>1)lc--;
	
	//反向输出
	for(int i=lc;i>=1;i--){
		cout<<c[i];
	} 
	return 0;
}

Kruskal算法(最小生成树)

先把边按照权值进行排序,用贪心的思想优先选取权值较小的边,并依次连接,若出现环则跳过此边(用并查集来判断是否存在环)继续搜,直到已经使用的边的数量比总点数少一即可。

#include
#include
#include
using namespace std;
long long n,m;
struct node{
	int start,end,v;
};
node edge[2000005];
int parent[5005],rank[5005];
//并查集路径压缩 
int find(int x){
	if(x==parent[x])return x;
	return parent[x]=find(parent[x]);
}
//秩优化 
void unio(int x,int y){
	int x_root=find(x);
	int y_root=find(y);
	if(x_root==y_root)return;
	if(rank[x_root]>rank[y_root]){
		parent[y_root]=x_root;
	}else{
		parent[x_root]=y_root;
		if(rank[x_root]==rank[y_root]){
			rank[y_root]++;
		}
	}
}
bool cmp(node a,node b){
	return a.v<b.v;
}
int main(){
	scanf("%lld%lld",&n,&m);
	//初始化并查集 
	for(int i=0;i<=n;i++){
		parent[i]=i;
		rank[i]=0;
	}
	for(int i=0;i<m;i++){
		scanf("%d %d %d",&edge[i].start,&edge[i].end,&edge[i].v);
	}
	//排序 
	sort(edge,edge+m,cmp);
	long long ans=0,con=0;
	//扫描边 
	for(int i=0;i<m;i++){
		//把祖宗都提前求出来减少复杂度 
		int eu=find(edge[i].start); 
		int ev=find(edge[i].end);
		if(eu!=ev){//不在同一个集合 
			ans+=edge[i].v;
			unio(eu,ev);//加入集合 
			con++;
			if(con==n-1)break;//n个顶点的最小生成树有n-1条边 
		}
	}
	if(con<n-1){
		cout<<"orz"<<endl;
	}else{
		printf("%lld\n",ans);
	}
	return 0;
}

传送门

BFS

DFS

你可能感兴趣的:(C/C++竞赛笔记,数据结构,算法,acm竞赛)