算法梳理 (CSP 2019

" 快要考CSP了 "

今年CSP没进TG(四川省喝了假酒

大概只能再骗一次普及了

整理一下算法模板吧

首先是较为简单的一些数论模板

EXGCD

#include<iostream>
using namespace std;
int X,Y;
int x1,y1; 
int gcd(int a,int b)
{
    return b ?  gcd(b,a%b) : a;
}
int exgcd(int a,int b,int &x1,int &y1) //附带求gcd的功能
{
    if(b==0){
        x=1;  //当 ax+by=gcd(a,b)中b=0时,a=gcd(a,b),x=1,y=0
        y=0;
        return a; //gcd功能实现,b=0时gcd(a,b)=a
    }
    int x2,y2;
    int t=exgcd(b,a%b,x2,y2);
    x1=y2;
    y1=x2-(a/b)*y2;
    return t;  //返回gcd(a,b)
}

int main()
{
    int a,b,c;
    cin>>a>>b>>c;
    int l=c/exgcd(a,b,x1,y1);
    X=x1*l;
    Y=y1*l;
    cout<<X<<" "<<Y<<endl;
}

埃氏筛

void work_prime(int n)
{
    for(int i=4;i<=n;i+=2){
        f[i]=true;
    }
    for(int i=3;i<=n;i++){
        if(!f[i]){
            for(int j=i*2;j<=n;j+=i){
                f[j]=true;
            }
        }
    }
}

不必过多赘述


接下来是一些基础图论算法的模板

二分图与匈牙利

#include<bits/stdc++.h>
using namespace std;
int ans=0;
const int maxn=1007;
bool vis[maxn];
int n,m;
int e;
bool g[maxn][maxn];
int match[maxn];
bool ap(int x){
    for(int j=1;j<=m;++j){//扫描每一个点 
        if(g[x][j]&&!vis[j]){//如果x,j之间存在一条边且j未被访问过
            vis[j]=true; //将j点标记为过
            if(!match[j]||ap(match[j])){ //如果这是增广路的中点或还能从这个点找到增广路 
                match[j]=x;  //变反 
                return true;//返回能找到 
            } 
        } 
    }
    return false; //如果扫描了每一个点都无法找到增广路就说明找不到增广路了 
}
void work()
{
    for(int i=1;i<=n;++i){ //枚举增广路起始点
        memset(vis,0,sizeof(vis));
        if(ap(i)){
            ans++;
        } 
    }
    return ;
}
int main()
{
    cin>>n>>m>>e;
    for(int i=1;i<=e;i++){
        int u,v;
        cin>>u>>v;
        g[u][v]=true; 
    }
    work();
    cout<<ans;
}

SPFA

#include <iostream>
#include <stdio.h>
#include <queue>
#include <vector>
#include <math.h>
using namespace std;
const int inf = 2147483647;
const int maxn = 10007;
const int maxm = 500007;
int dist[maxn],inQ[maxn]; 
vector<int> G[maxn];  //G[i][j] 代表以i点为起始点指向另一个点,的第j条边的编号 
queue<int> Q; //记录点的队列,用于一遍遍扫描整张图 
int m,n,S;
int U[maxm],V[maxm],C[maxm]; //编号为i的边的左端点,右端点,权值 
void spfa(int s) {
	for(int i=1;i<=m+1;++i) {
		dist[i]=inf;
	}
	dist[s] = 0;
	Q.push(s);
	while(!Q.empty()) {
		int u=Q.front();Q.pop();  //查找队头的点并将其取出 
	//	cout<<"u:"<<u<<" ";
		inQ[u]=0; //将其状态赋为不在队列中 
		for(int i=0;i<G[u].size();++i) {
			int e=G[u][i];
			//cout<<"e:"<<e<<" ";
			int v=V[e]^U[e]^u;  //其实可以直接用V[e]但这里以防万一(吃饱了撑着)还是这样打 
			if(dist[u]+C[e]<dist[v]) {
				dist[v]=dist[u]+C[e];
				if(!inQ[v]){
					Q.push(v);
					inQ[v]=1;
				}
			}
		}
		//cout<<endl;
	}
	return ;
}
int main() {
	cin>>m>>n>>S;
	for(int i=1;i<=n;i++){
		int u,v,c;
		cin>>u>>v>>c;
		G[u].push_back(i);
		U[i]=u;
		V[i]=v;
		C[i]=c; 
	}
	spfa(S);
	for(int i=1;i<=m;i++){
		cout<<dist[i]<<" ";
	}
	return 0;
}

kruskal

#include<bits/stdc++.h> 
const int MAX=1000007;
using namespace std;
int m,n;
long long ans=0;
struct edge
{
	int val;
	int l,r;
} e[MAX];
bool com(edge a,edge b)
{
	return a.val<b.val;
}
int fa[5001];
void init(int x)
{
	for(;x>=1;x--) 
		fa[x]=x;
}
int find(int x)
{
	if(x==fa[x]) return x;
	int k=find(fa[x]);
	fa[x]=k;
	return k;
}
bool merge(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return false;
	fa[xx]=yy;
	return true;
}
int main()
{
	cin>>n>>m;
	init(n); 
	for(int i=1;i<=m;++i){
		cin>>e[i].l>>e[i].r>>e[i].val;
	}
	sort(e+1,e+1+m,com);
	for(int i=1;i<=m;i++){
		if(merge(e[i].l,e[i].r)){
			ans+=e[i].val;
		}
	}
	for(int i=2;i<=n;i++){
		if(find(i)!=find(i-1)){
			cout<<"orz";
			return 0;
		}
	}
	cout<<ans;
}

dinic与网络最大流

#include<bits/stdc++.h>
using namespace std; 
int INF=1e9;
const int maxn=100007;
const int maxm=100007;
int l[maxn];  //记录当前点在增广路中的深度为深搜提供便利 
int head[maxn]; //head[u]记录u点的头指针 
struct edge
{
    int to;  //指向的下一个点的编号 
    int cost; //边权 
    int next; //指向的同一起始点的下一条边的编号 方便广搜遍历 
} es[maxm*2]; //记录边以及它的反弧 
int cnt=0; //记录边的个数即其编号 
void init(){
    for(int i=1;i<=maxn*2;i++) head[i]=-1;
}
void addedge(int u,int v,int cost) //添加边以及其反弧 
{
    es[cnt].to=v;
    es[cnt].cost=cost;
    es[cnt].next=head[u];//添加边 
    /*
    1.将新边的头指针指向原来U点的头指针  
    2.将原来U点的头指针指向新边
    3.并将新边的边权赋为cost 

    这三步操作即将此边放在链表的首位 
    */
    head[u]=cnt;  
    ++cnt;
    es[cnt].to=u;
    es[cnt].cost=0;
    es[cnt].next=head[v];  //添加反弧 
    /*
    进行与正边相反的操作
    并将反弧的初始权值赋为0 
    */
    head[v]=cnt;
    ++cnt;
}
bool ap(int s,int t)  //搜索是否能找到增广路 
{
    memset(l,0,sizeof(l));
    l[s]=1;
    queue<int> q; //利用队列存点更加便捷 
    q.push(s);
    while(!q.empty()) //广搜过程不需解释 
    {
        int u=q.front(); 
        q.pop();
        if(u==t) //如果搜索到了汇点 
            return  true;
        for(int v=head[u];v!=-1;v=es[v].next){
            int k=es[v].to;  //记录当前遍历到的下一个点 
            if(!l[k]&&es[v].cost){ //如果此点未被遍历且连接两个点的两条边权不为零 
                l[k]=l[u]+1;  
                q.push(k); 
            }
        } 
    }
    return false; //没有找到 
}
int dfs(int x,int t,int ans) //x为当前遍历到的点,t为汇点,ans为增广路的增益也是流量的限制 
{
    if(x==t){
        return ans;
    }
    int sum=0;
    for(int i=head[x];i!=-1;i=es[i].next){
        if(es[i].cost&&l[x]==l[es[i].to]-1){ //如果改边流量没有用完且下一个点在增广路上的下一个点 
            int f=dfs(es[i].to,t,min(es[i].cost,ans-sum));
            es[i].cost-=f;
            es[i^1].cost+=f; //反弧加上相应值
            sum+=f;
            if(sum==ans) 
                return sum; 
        }
    }
    return sum;
} 
int dinic(int s,int t) //s为源点,t为汇点 
{
    int ans=0;
    while(ap(s,t)) //重复找s到t的增广路
    {
        ans+=dfs(s,t,INF);
    } 
    return ans;
} 
int N,M,S,T; 
int main()
{
    init();
    cin>>N>>M>>S>>T;
    for(int i=1;i<=M;i++){
        int aa,bb,cc;
        cin>>aa>>bb>>cc;
        addedge(aa,bb,cc);
    }
    int ans=dinic(S,T);
    cout<<ans;
    return 0;
}


最后是字符串算法

KMP

#include<iostream>
using namespace std;
const int maxn=1000007;
char st[maxn]; //定义模式串 
char s[maxn];
int fail[maxn]; 
int siz=1;
void init()//初始化fail数组 
{
    fail[0]=0; //根据定义,fail[0]=0
    for(int i=1,j=0;st[i];i++){ //i是迭代变量,j存储fail[i-1]
        while(j&&st[i]!=st[j]){ //搜索合适的Border
            j=fail[j-1];
        }
        if(st[i]==st[j]){ //找到Border(i) 
            fail[i]=++j;
        }
        siz++;
    } 
}
int search(char *str) //KMP算法的主程序(搜索匹配)部分,str为主串 
{
    int ans=0;
    for(int i=0,j=0;str[i];i++){ //i为主串指针,j为模式串指针 
        while(j&&str[i]!=st[j]) //搜索合适的Border
            j=fail[j-1];
        if(str[i]==st[j]){
            if(!st[++j]){
                ans++;
            }
        } 
    }
    return ans; 
}
int main()
{
    cin>>s;
    cin>>st;
    init();
    int ans=search(s);  //search函数的返回值为出现的次数 
    cout<<ans<<endl; 
} 

end

原文地址:https://www.cnblogs.com/XJack/p/11791752.html