zoukankan      html  css  js  c++  java
  • 算法梳理 (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

  • 相关阅读:
    几个简单的定律
    poj 2443 Set Operation 位运算
    博弈论 wythff 博弈
    BZOJ 2120 树状数组套平衡树
    HDU 1392 凸包
    ZOJ 1648 线段相交
    HDU 1756 点在多边形内
    SPOJ 1811 LCS 后缀自动机
    BZOJ 1901 树状数组+函数式线段树
    HDU 1086 线段相交(不规范相交模板)
  • 原文地址:https://www.cnblogs.com/XJack/p/11791752.html
Copyright © 2011-2022 走看看