这是我经过两年ACM竞赛的所有算法总结,希望对你有帮助。

目录

最短路

Floyd

Dijkstra

SPFA

最小生成树

Kruskal

Prim

动态规划

01背包

完全背包

多重背包

最长公共子序列

单调递增子序列

单调递增子序列(二分)

字符串匹配

KMP

字典树

AC自动机

博弈

巴什博奕(Bash Game)

威佐夫博弈(Wythoff Game)

尼姆博弈(Nimm Game)

大数

浮点大数加法

大数乘法

大数开方

Hash(除留余数法+链地址法)

堆排序(最小堆)

拓扑排序

归并排序

二分匹配

并查集

最大流

欧拉函数

扩展欧几里得

费马小定理


最短路

Floyd

该算法的时间复杂度是O(n*n*n),可以解决负权边的问题
举一个例子吧,给你n个点,m条双向边,求从1点到各个点的最短路径
样例:

6 9
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<stdio.h>
#define inf 99999999
void floyd();
int e[110][110],m,n;
int main()
{int i,j,a,b,c;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j)e[i][j]=0;//自己到自己的路程为0elsee[i][j]=inf;//刚开始初始化为最大值,默认为该路不通for(i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);if(c<e[a][b]){e[a][b]=c;e[b][a]=c;}    }floyd();for(i=1;i<=n;i++)printf("%d ",e[1][i]);    printf("\n");}return 0;
}
void floyd()
{int i,j,k;for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(e[i][j]>e[i][k]+e[k][j])e[i][j]=e[i][k]+e[k][j];
}

Dijkstra

该算法的时间复杂度是O(m+n)*logn,不可以解决负权边的问题
举一个例子吧,给你n个点,m条双向边,问从1点到各个点的最短路径
样例输入:

6 9
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<stdio.h>
#include<string.h>
void dijkstra();
int e[110][110],dis[110],book[110];
int n,m;
#define inf 99999999
int main()
{int i,j,a,b,c;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j)e[i][j]=0;elsee[i][j]=inf;for(i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);if(c<e[a][b]){e[a][b]=c;e[b][a]=c;}}    for(i=1;i<=n;i++)dis[i]=e[1][i];//把从1点出发到各个点的值先存到dis数组里,先当作最短路memset(book,0,sizeof(book));book[1]=1;dijkstra();for(i=1;i<=n;i++)printf("%d ",dis[i]);printf("\n");}    return 0;
}
void dijkstra()
{int i,j,k,min,u;for(k=1;k<=n-1;k++){min=inf;for(i=1;i<=n;i++)if(book[i]==0&&dis[i]<min){min=dis[i];u=i;}book[u]=1;for(j=1;j<=n;j++)if(dis[j]>dis[u]+e[u][j])dis[j]=dis[u]+e[u][j];}
}

SPFA

该算法的时间复杂度最坏是O(m*n),可以解决负权边的问题
举一个例子吧,给你n个点,m条单向边,问从1点到各个点的最短路径
队列优化:
样例输入:

6 9
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<stdio.h>
#include<string.h>
#include<queue>
using namespace std;const int inf=1e9;int n,m;
int e[110][110];
int dis[110],book[110];void SPFA();int main()
{int i,j,u,v,w;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=n;i++)for(j=1;j<=n;j++){if(i==j)e[i][j]=0;elsee[i][j]=inf;}for(i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);if(w<e[u][v])e[u][v]=w;}SPFA();for(i=1;i<=n;i++)printf("%d ",dis[i]);printf("\n");}return 0;
}
void SPFA()
{int i,u,v;memset(book,0,sizeof(book));for(i=1;i<=n;i++)dis[i]=inf;dis[1]=0;queue<int>q;q.push(1);book[1]=1;while(!q.empty()){u=q.front();q.pop();book[u]=0;for(v=1;v<=n;v++){if(e[u][v]!=inf&&dis[u]+e[u][v]<dis[v]){dis[v]=dis[u]+e[u][v];if(book[v]==0){q.push(v);book[v]=1;}}}}
}

最小生成树

Kruskal

给你n个点m条边及各边的权值,求最小生成树
样例输入:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
样例输出:
19
程序代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int kruskal();
int getf(int u);
int merge(int u,int v);
int cmp(struct data x,struct data y);
int f[110],n,m;
struct data{int u;int v;int w;
}e[110];
int main()
{int i;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=m;i++)scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);sort(e+1,e+m+1,cmp);for(i=1;i<=n;i++)f[i]=i;printf("%d\n",kruskal());}return 0;
}
int kruskal()
{int i,sum=0,count=0;for(i=1;i<=m;i++){if(merge(e[i].u,e[i].v)==1)如果连接这两点,则建立该边{count++;sum+=e[i].w;    }     if(count==n-1)//如果建立了n-1条边,则n个点全部连完return sum;}
}
int getf(int u)//并查集寻找共同祖先
{if(f[u]==u)return u;f[u]=getf(f[u]);return f[u];
}
int merge(int u,int v)//合并两个集合
{u=getf(u);v=getf(v);if(u!=v){f[v]=u;return 1;    }return 0;
}
int cmp(struct data x,struct data y)
{return x.w<y.w;
}

Prim

给你n个点m条边及各边的权值,求最小生成树
样例输入:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
样例输出:
19
程序代码:

#include<stdio.h>
#include<string.h>
#define inf 0x7f7f7f7f
int prim();
int n,m;
int e[110][110],book[110],dis[110];
int main()
{int i,j,a,b,c;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j)e[i][j]=0;elsee[i][j]=inf;for(i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);if(c<e[a][b])e[a][b]=e[b][a]=c;}    printf("%d\n",prim());}return 0;
}
int prim()
{int i,k,min,u,sum=0;memset(book,0,sizeof(book));for(i=1;i<=n;i++)dis[i]=e[1][i];book[1]=1;for(k=1;k<=n-1;k++){min=inf;for(i=1;i<=n;i++)if(book[i]==0&&dis[i]<min){min=dis[i];u=i;}book[u]=1;sum+=dis[u];for(i=1;i<=n;i++)if(book[i]==0&&dis[i]>e[u][i])dis[i]=e[u][i];}    return sum;
}

动态规划

01背包

给你n种物品每种物品有一件和一个容量为m的背包
然后给你每种物品的体积和价值
求背包所能容下的最大价值
样例输入
3 8
4 3
3 2
2 1
样例输出
5
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],dp[110];
int main()
{int i,j,n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(dp,0,sizeof(dp));for(i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);for(i=1;i<=n;i++)for(j=m;j>=v[i];j--)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);printf("%d\n",dp[m]);}return 0;
}

完全背包

给你n种物品每种物品有无穷多件和一个容量为m的背包
然后给你每种物品的体积和价值
求背包所能容下的最大价值

样例输入

3 8
4 3
3 2
2 1
样例输出
6
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],dp[110];
int main()
{int n,m,i,j;while(scanf("%d%d",&n,&m)!=EOF){memset(dp,0,sizeof(dp));for(i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);for(i=1;i<=n;i++)for(j=v[i];j<=m;j++)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);printf("%d\n",dp[m]);}return 0;
}

多重背包

给你n种物品每种物品有多件和一个容量为m的背包
然后给你每种物品的体积、价值和数量
求背包所能容下的最大价值

样例输入

3 10
4 3 3
3 2 2
2 1 1

样例输出

7

程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],c[110],dp[110];
int main()
{int n,m,i,j,k;while(scanf("%d%d",&n,&m)!=EOF){memset(dp,0,sizeof(dp));for(i=1;i<=n;i++)scanf("%d%d%d",&v[i],&w[i],&c[i]);for(i=1;i<=n;i++)for(j=1;j<=c[i];j++)for(k=m;k>=v[i];k--)dp[k]=max(dp[k],dp[k-v[i]]+w[i]);printf("%d\n",dp[m]);}return 0;
}

最长公共子序列

样例输入:
2
asdf
adfsd
123abc
abc123abc
样例输出:
3
6
程序代码:

#include<stdio.h>
#include<string.h>
int dp[1010][1010];
int Max(int a,int b)
{if(a>b)return a;return b;
}
int main()
{int n,i,j,len1,len2;char str1[1010],str2[1010];scanf("%d",&n);while(n--){memset(dp,0,sizeof(dp));scanf("%s%s",str1,str2);len1=strlen(str1);len2=strlen(str2);for(i=1;i<=len1;i++)for(j=1;j<=len2;j++){if(str1[i-1]==str2[j-1])dp[i][j]=dp[i-1][j-1]+1;elsedp[i][j]=Max(dp[i-1][j],dp[i][j-1]);}printf("%d\n",dp[len1][len2]);}return 0;
}

单调递增子序列

#include<stdio.h>
#include<string.h>
char str[10010];
int dp[10010];
int main()
{int n,i,j,maxn,len;scanf("%d",&n);while(n--){scanf("%s",str);len=strlen(str);for(i=0;i<len;i++)dp[i]=1;for(i=1;i<len;i++)for(j=0;j<i;j++)if(str[i]>str[j]&&dp[j]+1>dp[i])dp[i]=dp[j]+1;maxn=dp[0];for(i=1;i<len;i++)if(dp[i]>maxn)maxn=dp[i];printf("%d\n",maxn);}return 0;
}

单调递增子序列(二分)

样例输入:
7
1 9 10 5 11 2 13
2
2 -1
样例输出:
5
1
程序代码:

#include<stdio.h>
#include<string.h>
int Two(int start,int end,int x);
int a[100010],dp[100010];
int main()
{int n,i,j,t,len;while(scanf("%d",&n)!=EOF){memset(dp,0,sizeof(dp));for(i=0;i<n;i++)scanf("%d",&a[i]);dp[1]=a[0];len=1;for(i=1;i<n;i++){t=Two(1,len,a[i]);//通过二分搜索查找a[i]要插入的位置    dp[t]=a[i];if(t>len)len=t;}printf("%d\n",len);}return 0;
}
int Two(int start,int end,int x)
{int mid;while(start<=end){mid=(start+end)/2;if(x==dp[mid])return mid;if(x>dp[mid])start=mid+1;if(x<dp[mid])end=mid-1;}return start;
}

字符串匹配

KMP

Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
题意描述:
找出a串中包含多少个b串
程序代码:

#include<stdio.h>
#include<string.h>
void get_next();
int next[10010],m,n;
char a[1000010],b[10010];
int main()
{int T,i,j,count;scanf("%d",&T);while(T--){scanf("%s%s",b,a);n=strlen(a);m=strlen(b);get_next();i=0;j=0;count=0;while(i<n){if(j==0&&a[i]!=b[j])i++;else if(j>0&&a[i]!=b[j])j=next[j-1];else{i++;j++;}if(j==m){j=next[j-1];count++;}}printf("%d\n",count);}return 0;
}
void get_next()
{int i,j;i=1;j=0;next[0]=0;while(i<m){if(j==0&&b[i]!=b[j]){next[i]=0;i++;}else if(j>0&&b[i]!=b[j])j=next[j-1];else{next[i]=j+1;i++;j++;}}
}

字典树

Sample Input
 
banana
band
bee
absolute
acm
 
ba
b
band
abc
Sample Output
 
2
3
1
0
题意描述:
给你多个单词,然后给你多个前缀,让你找具有该前缀的单词的个数,首先建立一个字典树,把所有的单词存里面,然后逐个输入前缀并逐个判断
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct Trie{int v;Trie *next[26];
};
Trie root;
void get_trie(char *str);
int find_trie(char *str);
int main()
{int i,len;char str[15];for(i=0;i<26;i++)root.next[i]=NULL;while(fgets(str,15,stdin),str[0]!='\n'){len=strlen(str);//用fgets多了一个\nstr[len-1]='\0';get_trie(str);}while(scanf("%s",str)!=EOF){printf("%d\n",find_trie(str));}return 0;
}
void get_trie(char *str)
{int i,j,len,id;Trie *p=&root,*q;len=strlen(str);for(i=0;i<len;i++){id=str[i]-'a';if(p->next[id]==NULL){q=(Trie*)malloc(sizeof(root));q->v=1;for(j=0;j<26;j++)q->next[j]=NULL;p->next[id]=q;p=p->next[id];}else{p=p->next[id];p->v++;}}
}
int find_trie(char *str)
{int i,len,id;Trie *p=&root;len=strlen(str);for(i=0;i<len;i++){id=str[i]-'a';p=p->next[id];if(p==NULL)return 0;}return p->v;
}

AC自动机

Sample Input
1
5
she
he
say
shr
her
yasherhs
Sample Output
3
题意描述:
给你n个单词和一篇文章,找出这n个单词在文章中共出现多少次
程序代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
using namespace std;struct node{node *fail;node *next[26];int countn;
};
node *root;
char str[55];
char s[1000005];void GetTrie(char *str);
void BuildAcAutomation();
int query();int main()
{int len,i,T,n;scanf("%d",&T);while(T--){root=new node();scanf("%d",&n);while(n--){scanf("%s",str);GetTrie(str);}scanf("%s",s);BuildAcAutomation();printf("%d\n",query());}return 0;
}
void GetTrie(char *str)
{node *p;int i,id;p=root;for(i=0;str[i]!='\0';i++){id=str[i]-'a';if(p->next[id]==NULL)p->next[id]=new node();p=p->next[id];}p->countn++;//代表单词数加1//printf("***%d\n",p->countn);
}
void BuildAcAutomation()
{int i;node *temp,*p;p=new node();queue<node*>q;q.push(root);while(!q.empty()){temp=q.front();q.pop();for(i=0;i<26;i++){if(temp->next[i]!=NULL){if(temp==root)temp->next[i]->fail=root;else{p=temp->fail;while(p!=NULL){if(p->next[i]!=NULL){temp->next[i]->fail=p->next[i];break;}p=p->fail;}if(p==NULL)temp->next[i]->fail=root;}q.push(temp->next[i]);}}}
}
int query()
{int i,id,sum=0;node *p,*temp;p=root;for(i=0;s[i]!='\0';i++){id=s[i]-'a';while(p->next[id]==NULL&&p!=root)p=p->fail;p=p->next[id];if(p==NULL)p=root;temp=p;while(temp!=root&&temp->countn!=0){sum+=temp->countn;temp->countn=0;temp=temp->fail;}}return sum;
}

博弈

巴什博奕(Bash Game)

只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜
举一个最简单的例子就是,当n=m+1时,此时不管先手取多少,后手都能把剩下的取完,拓展到n等于m+1的倍数时,不管先手取多少,后手都可以取(m+1减去先手取的个数)个,最后先手一定会面临n=m+1的情况,此时先手必败,否则先手必胜
程序代码:

#include<stdio.h>
int main()
{int n,m;while(scanf("%d%d",&n,&m)!=EOF){if(n%(m+1)==0)  printf("先手必败\n");elseprintf("先手必胜\n");}return 0;
}

威佐夫博弈(Wythoff Game)

有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利

这有一个公式:t=(int)((a-b)*(1.0+sqrt(5.0))/2.0);让t与较小堆石子数相比,如果相等先手必败,否则先手必胜

程序代码:

#include<stdio.h>
#include<math.h>
int main()
{int a,b,t;while(scanf("%d%d",&a,&b)!=EOF){if(a<b)  {a^=b;b^=a;a^=b;}t=(int)((a-b)*(1.0+sqrt(5.0))/2.0);if(t==b)printf("先手必败\n");elseprintf("先手必胜\n");    }return 0;
}

尼姆博弈(Nimm Game)

有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜
把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜
程序代码:

#include<stdio.h>
int main()
{int n,m,i,count;while(scanf("%d",&n)!=EOF){count=0;for(i=0;i<n;i++){scanf("%d",&m);count^=m;}if(count==0)  printf("先手必败\n");elseprintf("先手必胜\n");}return 0;
}

大数

浮点大数加法

#include<stdio.h>
#include<string.h>
int Max(int a,int b);
int main()
{char str1[410],str2[410];int max1[410],min1[410],max2[410],min2[410];int i,l1,l2,p,q,m,n;while(scanf("%s%s",str1,str2)!=EOF){memset(max1,0,sizeof(max1));memset(max2,0,sizeof(max2));memset(min1,0,sizeof(min1));memset(min2,0,sizeof(min2));l1=strlen(str1);l2=strlen(str2);p=0;q=0;for(i=0;str1[i]!='\0';i++){if(str1[i]=='.')break;elsep++;}for(i=0;i<p;i++)max1[i]=str1[p-1-i]-'0';for(i=0;i<l1-1-p;i++)min1[i]=str1[p+1+i]-'0';for(i=0;str2[i]!='\0';i++){if(str2[i]=='.')break;elseq++;}for(i=0;i<q;i++)max2[i]=str2[q-1-i]-'0';for(i=0;i<l2-1-q;i++)min2[i]=str2[q+1+i]-'0';m=Max((l1-p-1),(l2-q-1));for(i=m-1;i>0;i--){min1[i]+=min2[i];if(min1[i]>9){min1[i]%=10;min1[i-1]++;}}min1[0]+=min2[0];if(min1[0]>9){min1[0]%=10;max1[0]++;}n=Max(p,q);for(i=0;i<n;i++){max1[i]+=max2[i];if(max1[i]>9){max1[i]%=10;max1[i+1]++;}}if(max1[n]>0)for(i=n;i>=0;i--)printf("%d",max1[i]);elsefor(i=n-1;i>=0;i--)printf("%d",max1[i]);while(1){if(min1[m-1]==0)m--;elsebreak;}if(m>0)printf(".");for(i=0;i<m;i++)printf("%d",min1[i]);printf("\n");}return 0;
}int Max(int a,int b)
{if(a>b)return a;elsereturn b;
}

大数乘法

#include<stdio.h>
#include<string.h>
int main()
{char str1[110],str2[110];int a[110],b[110],c[220],d[220];int len1,len2,i,j,k,t;scanf("%s%s",str1,str2);len1=strlen(str1);len2=strlen(str2);for(i=0;i<len1;i++)a[i]=str1[len1-1-i]-'0';for(i=0;i<len2;i++)b[i]=str2[len2-1-i]-'0';memset(c,0,sizeof(c));for(i=0;i<len1;i++)for(j=0;j<len2;j++)c[i+j]+=a[i]*b[j];t=j=0;for(i=0;i<len1+len2-1;i++){d[j++]=(c[i]+t)%10;t=(c[i]+t)/10;}while(t){d[j++]=t%10;t/=10;}for(i=j-1;i>=0;i--)printf("%d",d[i]);return 0;
}

大数开方

#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define DEPTH 10typedef int BigInteger[10100];int comp(const BigInteger a,const int c,const int d,const BigInteger b) //大数比较
{int i,t=0,O=-DEPTH*2;if(b[0]-a[0]<d&&c) return 1;for(i=b[0];i>d;i--){t=t*DEPTH+a[i-d]*c-b[i];if(t>0) return 1;if(t<O) return 0;}for(i=d;i;i--){t=t*DEPTH-b[i];if(t>0) return 1;if(t<O) return 0;}return t>0;
}void sub(BigInteger a,const BigInteger b,const int c,const int d) //大数减
{int i,O=b[0]+d;for(i=1+d;i<=O;i++)if((a[i]-=b[i-d]*c)<0)a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH;for(;a[i]<0;a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH,i++);for(;!a[a[0]]&&a[0]>1;a[0]--);
}void Sqrt(BigInteger b,BigInteger a) //开平方
{int h,l,m,i;memset((void*)b,0,sizeof(BigInteger));for(i= b[0]=(a[0]+1)>>1;i;sub(a,b,m,i-1),b[i]+=m,i--)for(h=DEPTH-1,l=0,b[i]=m=(h+l+1)>>1;h>l;b[i]=m=(h+l+1)>>1)if(comp(b,m,i-1,a)) h=m-1;else l = m;for(;!b[b[0]]&&b[0]>1;b[0]--);for (i = 1; i <= b[0]; b[i++] >>= 1);
}char str[10100];
BigInteger a,b;int main()
{while(scanf("%s",str)!=EOF){a[0]=strlen(str);for(int i=1; i<=a[0]; i++)a[i]=str[a[0]-i]-'0';Sqrt(b,a);for(int i=b[0]; i>=1; i--)printf("%d",b[i]);printf("\n");}return 0;
}

Hash(除留余数法+链地址法)

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int N=10;struct Node{int num;Node *next;
};
struct HashTable{Node *element[N];int countn;
};HashTable *p;void InsertHashTable(int key);
int Search(int key);int main()
{int i,n;int a[N]={12,45,2,6,78,9,0,1,15,18};p=new HashTable();//这个先是初始化p这个头指针for(i=0;i<N;i++)p->element[i]=new Node();//这是初始化p下的每个element指针for(i=0;i<N;i++)InsertHashTable(a[i]);while(scanf("%d",&n)!=EOF)//可输入待查找元素{printf("%d\n",Search(n));}return 0;
}
void InsertHashTable(int key)
{Node *q;q=new Node();q->num=key;q->next=p->element[key%N]->next;p->element[key%N]->next=q;
}
int Search(int key)
{Node *q;q=new Node();q=p->element[key%N]->next;while(q!=NULL){if(q->num==key)return key%N;q=q->next;}return -1;
}

堆排序(最小堆)

该排序首先要建立堆以建立最小堆为例,在建立堆的时候用到了向下调整的思想,即该点如果的值a[i],如果小于a[2*i]或a[2*+1],则需要把a[i]与a[2*i]和a[2*+1]的较小者交换,以达到上小下大,需要注意的是在建立最小堆的时候要从后往前建立,也就是最后一个非叶子节点开始建立,即a[n/2],因为这样可以达到下面的数都比上面的数大(如果从根开始建堆的话,可能会出现最下面的数很小,但是中间的数较大,然后中上的数又较小,而最上面的数很大,这样一来,最上面的数与中上换过之后不能与中间的数交换,以导致最下面的最小数无法换到上面,下面给的样例就是这样的)

样例输入:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
样例输出:
1 2 5 7 12 17 19 22 25 28 36 46 92 99
程序代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int a[110];
int n;
void siftdown(int i);//向下调整
int main()
{int i,j,maxn=-1;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&a[i]);if(a[i]>maxn)maxn=a[i];}for(i=n/2;i>=1;i--)//建立堆siftdown(i);for(i=1;i<=n;i++){printf("%d ",a[1]);a[1]=maxn;siftdown(1);}printf("\n");return 0;
}
void siftdown(int i)//最小堆
{int t,flag=0,temp;while(2*i<=n&&flag==0){t=i;if(a[t]>a[2*i])t=2*i;if(2*i+1<=n&&a[t]>a[2*i+1])t=2*i+1;if(t!=i){temp=a[i];a[i]=a[t];a[t]=temp;i=t;}elseflag=1;}
}

拓扑排序

每次都是把入度为0的点输出,当然输出之后会更新其他点的入度
样例
6 8
a b
a c
a d
c b
c d
d e
f d
f e
输出
a c b f d e
程序代码:

#include<stdio.h>
#include<queue>
using namespace std;int a[110][110],b[110];
int main()
{priority_queue<int,vector<int>,greater<int> >q;//greater<int> 后有一个空格char x,y;int A;int i,n,m;scanf("%d%d",&n,&m);for(i=0;i<m;i++){scanf(" %c %c",&x,&y);a[x-'a'][y-'a']++;b[y-'a']++;}for(i=0;i<n;i++)if(b[i]==0)q.push(i);while(!q.empty()){A=q.top();q.pop();printf("%c ",A+'a');for(i=0;i<n;i++)if(a[A][i]>0){b[i]--;if(b[i]==0)q.push(i);}}return 0;
}

归并排序

#include<stdio.h>
int a[110],temp[110];void MergeSort(int l,int r);
void Merge(int l,int mid,int r);int main()
{int n,i;scanf("%d",&n);for(i=0;i<n;i++)scanf("%d",&a[i]);MergeSort(0,n-1);for(i=0;i<n;i++)printf("%d ",a[i]);return 0;
}
void MergeSort(int l,int r)
{int mid;if(l<r){mid=(l+r)/2;MergeSort(l,mid);MergeSort(mid+1,r);Merge(l,mid,r);}
}
void Merge(int l,int mid,int r)
{int i,j,k;i=l;j=mid+1;k=l;while(i<=mid&&j<=r){if(a[i]>a[j])temp[k++]=a[j++];elsetemp[k++]=a[i++];}while(i<=mid)temp[k++]=a[i++];while(j<=r)temp[k++]=a[j++];for(i=l;i<=r;i++)a[i]=temp[i];
}

二分匹配

二分匹配举个简单的例子,有m个女生n个男生k组关系代表着该女生和该男生可以在一起,目的是达到最大的匹配对数,可以用二分匹配来找最大匹配数,大致过程就是从第一个女生开始找,找到一个与她有关系的男生后,开始找下一个女生,如果下一个女生也与该男生有关系,那么就让该女生找其他的男生,如果找到了那么该女生就把该男生让给下一个女生,如果找不到,那么下一个女生就去找下一个男生,一直这样把所有的女生都找一遍,即可得到最大匹配数
样例输入:
3 3 4
1 2
1 3
2 1
2 2
样例输出:
2
程序代码:

#include<stdio.h>
#include<string.h>
int book[110],e[110][110],match[110],m,n,k;
int dfs(int u);
int hungry();
int main()
{int i,j,a,b;while(scanf("%d%d%d",&m,&n,&k)!=EOF){memset(e,0,sizeof(e));for(i=1;i<=k;i++){scanf("%d%d",&a,&b);e[a][b]=1;}printf("%d\n",hungry());}    return 0;
}
int dfs(int u)
{int i;for(i=1;i<=n;i++)if(book[i]==0&&e[u][i]==1){book[i]=1;if(match[i]==0||dfs(match[i])==1){match[i]=u;return 1;}}return 0;
}
int hungry()
{int i,sum=0;memset(match,0,sizeof(match));for(i=1;i<=m;i++){memset(book,0,sizeof(book));if(dfs(i)==1)sum++;}return sum;
}

并查集

n个强盗,m表示警方的线索,接下来m行,每行两个整数a,b表示a强盗和b强盗是同伙,问一共有多少个团伙
样例输入:
10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
样例输出:
3

#include<stdio.h>
int getf(int v);
void merge(int u,int v);
int f[110];
int main()
{int i,n,m,x,y,sum=0;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)f[i]=i;for(i=1;i<=m;i++){scanf("%d%d",&x,&y);merge(x,y);}for(i=1;i<=n;i++)if(f[i]==i)sum++;printf("%d\n",sum);return 0;
}
int getf(int v)
{if(v==f[v])return v;f[v]=getf(f[v]);return f[v];
}
void merge(int u,int v)
{u=getf(u);v=getf(v);if(u!=v)f[v]=u;return;
}

最大流

首先是bfs,这是对整个图进行分层,默认后一层等于前一层加1
然后就是dfs,每次搜索,因为之前对图已经分层,所以直接可以按层进行深搜,直到找到n为止
最后就是Dinic了,每次增广找到最短的一条边,并且把所有的正向边减少a反向边增加a
样例输入:
6 7
1 2 2
1 3 1
2 5 1
2 4 2
3 4 1
4 6 2
5 6 1
样例输出:
3
程序代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int inf=1e9;int n,m;
int e[110][110];
int dis[110];
int bfs(int s);
int Find(int x,int minn);
int Dinic();int main()
{int i,u,v,w;while(scanf("%d%d",&n,&m)!=EOF){memset(e,0,sizeof(e));for(i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);e[u][v]=w;}printf("%d\n",Dinic());}return 0;
}int bfs(int s)
{int A,i;memset(dis,-1,sizeof(dis));queue<int>q;q.push(s);dis[1]=0;while(!q.empty()){A=q.front();q.pop();for(i=1;i<=n;i++){if(dis[i]==-1&&e[A][i]>0){dis[i]=dis[A]+1;q.push(i);}}}if(dis[n]>0)return 1;return 0;
}int dfs(int x,int minn)
{int i,a=0;if(x==n)return minn;for(i=1;i<=n;i++){if(e[x][i]>0&&dis[i]==dis[x]+1&&(a=dfs(i,min(minn,e[x][i])))){e[x][i]-=a;e[i][x]+=a;return a;}}return 0;
}int Dinic()
{int temp,sum=0;while(bfs(1)){while(temp=dfs(1,inf)){sum+=temp;}}return sum;
}

欧拉函数

//欧拉函数是小于n的正整数中与n互质的数的数目
#include<stdio.h>
#include<stdlib.h>
int eular(int n);int main ()
{int n;scanf("%d",&n);printf("%d",eular(n));return 0;
}
int eular(int n)
{int ret=1,i;for(i=2;i*i<=n;i++){if(n%i==0){n/=i,ret*=i-1;while(n%i==0) n/=i,ret*=i;}}if(n>1)ret*=n-1;return ret;
}

扩展欧几里得

已知a, b求解一组x,y,ax+by = gcd(a, b) =d(解一定存在)
gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)
扩展欧几里得求逆元
什么是逆元

当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:

设c是b的逆元,则有b*c≡1(mod m);

则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);

即a/b的模等于a*b的逆元的模;
A/B
Problem Description
要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

Input
数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

Output
对应每组数据输出(A/B)%9973。

Sample Input
2
1000 53
87 123456789

Sample Output
7922
6060

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){if(!b){x=1;y=0;return a;}ll d=exgcd(b,a%b,x,y);ll tmp=x;x=y;y=tmp-a/b*y;return d;
}
ll inv(ll a,ll m){ll x,y;ll d=exgcd(a,m,x,y);if(d==1){//处理负数return (x%m+m)%m;}return -1;
}
ll n,b;
int t;
const ll MOD=9973;
int main(void){scanf("%d",&t);while(t--){scanf("%lld%lld",&n,&b);ll c=inv(b,MOD);printf("%lld\n",n*c%MOD);}return 0;
}

费马小定理

p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)
莫比乌斯函数
莫比乌斯函数,由德国数学家和天文学家莫比乌斯提出。梅滕斯(Mertens)首先使用μ(n)(miu(n))作为莫比乌斯函数的记号。(据说,高斯(Gauss)比莫比乌斯早三十年就曾考虑过这个函数)。
具体定义如下:
如果一个数包含平方因子,那么miu(n) = 0。例如:miu(4), miu(12), miu(18) = 0。
如果一个数不包含平方因子,并且有k个不同的质因子,那么miu(n) = (-1)^k。例如:miu(2), miu(3), miu(30) = -1,miu(1), miu(6), miu(10) = 1。
给出一个数n, 计算miu(n)。
输入
输入包括一个数n,(2 <= n <= 10^9)
输出
输出miu(n)。
输入样例
5
输出样例
-1

#include<stdio.h>
int num;
int miu(int n)
{int i;for(i=2;i*i<=n;i++){if(n%i==0){n=n/i;num++;if(n%i==0)return 0;}}num++;if(num%2==0)return 1;return -1;
}
int main()
{int n;while(scanf("%d",&n)!=EOF){num=0;printf("%d\n",miu(n));    }return 0;
}

题意:对于 a, b, c, d, k . 有 x 属于 [a, b],  y 属于 [c, d], 求 gcd(x, y) = k 的 x, y 的对数 . 可以假设在所有测试用例中a = c = 1。
注意: (x, y), (y, x) 算一种情况 .

#include <iostream>
#include <stdio.h>
#include <string.h>
#define ll long long
using namespace std;const int MAXN = 1e6 + 10;bool check[MAXN];
int mu[MAXN], prime[MAXN];void Moblus(void){memset(check, false, sizeof(check));int tot = 0;mu[1] = 1;for(int i = 2; i < MAXN; i++){if(!check[i]){prime[tot++] = i;mu[i] = -1;}for(int j = 0; j < tot && i * prime[j] < MAXN; j++){check[i * prime[j]] = true;if(i % prime[j] == 0){mu[i * prime[j]] = 0;break;}else mu[i * prime[j]] = -mu[i];}}
}int main(void){int t, a, b, c, d, k;Moblus();scanf("%d", &t);for(int i = 1; i <= t; i++){scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);if(k == 0){printf("Case %d: 0\n", i);continue;}if(b > d) swap(b, d);b /= k;d /= k;ll ans1 = 0, ans2 = 0;for(int j = 1; j <= b; j++){ans1 += (ll)mu[j] * (b / j) * (d / j);}for(int j = 1; j <= b; j++){ans2 += (ll)mu[j] * (b / j) * (b / j);}printf("Case %d: %lld\n",i, ans1 - (ans2 >> 1));}return 0;
}

两年ACM竞赛的所有算法总结相关推荐

  1. ACM如何入门,ACM竞赛需要学习哪些算法?

    #################成绩################## 大一:2017年4月份"玲珑杯"河南工业大学2017年ACM程序设计大赛暨河南高校邀请赛,获得银奖 20 ...

  2. 【退役贴】再见了ACM,再会了算法竞赛

    目录 写在前面 心里话 ACM是什么 回顾省赛&昆明 收获 付出 选择ACM竞赛的原因 对一些ACMer的话 之后的打算 写在前面 心里话 2022ICPC昆明站告一段落,随之结束的是我两年多 ...

  3. 退役帖:再见ACM/ICPC!再见算法竞赛!

    结束啦!手指放在键盘上停了好久,却不知从何谈起,那些一幕一幕,又重新浮现眼前.以前很喜欢看一些ACMer的退役帖,有金牌final的,也有铜铁退役,无关成绩,单纯那些奋斗的过程,努力的开心让我很感动. ...

  4. 做acm 需要学的算法

    做acm 需要学的算法 转一个搞ACM需要的掌握的算法.  要注意,ACM的竞赛性强,因此自己应该和自己的实际应用联系起来.  适合自己的才是好的,有的人不适合搞算法,喜欢系统架构,因此不要看到别人什 ...

  5. ACM竞赛学习整理--矩阵运算

    ACM竞赛学习整理–矩阵运算 了解矩阵类 [任务] 实现矩阵的基本变换 [接口] 结构体:Matrix 成员变量: int n,m 矩阵大小 int a[][] 矩阵内容 重载运算符: +.-.x 成 ...

  6. ACM竞赛学习整理开篇之01背包问题

    ACM竞赛学习整理开篇之01背包问题. 最近,偶然的一次机会让我关注信息奥赛的一些内容.发现其中的内容很有趣,是学习编程的一条很好的路径,又能很好地将数学和编程联系到一起.在csdn里看到了不少同好也 ...

  7. 河南省第十二届ACM竞赛总结

    昨天下午紧张刺激的省ACM竞赛结束了,五个小时的大脑超频运转,五十多个学校的二百三十一支队伍在黄淮学院体育场的激烈角逐,这次比上次的CCPC结果要好,一个多月的辛苦总算没有付诸东流,最终我们队取得铜奖 ...

  8. 2021年ACM竞赛班训练(六)题解

    2021年ACM竞赛班训练(六)题解 ==Problem A 逆元== 题目描述 输入 输出 题目分析 代码 ==Problem B&C 五一假期前最后一题&女神的考验== 题目描述 ...

  9. ACM 中常用的算法有哪些?

    在网上看到别人ACM学习的心得,转载过来,源地址不记得了,当时是百度的.内容如下: 网络上流传的答案有很多,估计提问者也曾经去网上搜过.所以根据自己微薄的经验提点看法. 我ACM初期是训练编码能力,以 ...

最新文章

  1. linux下的hive命令大全,Hive shell 常用命令
  2. 数据驱动的未来城市八大趋势
  3. 【注释规约】规范化的第一步,你类和方法的注释,规范嘛?
  4. 零基础也能看懂python_零基础也能看懂的Python下载网易云音乐爬虫
  5. 开源软件公司易犯的 5 大错误,又该如何避免?
  6. Visual Studio可视化IDE风格主题参照
  7. docker使用阿里云镜像仓库
  8. 如何构建自己的Java学习体系?
  9. PowerPoint 中缺少think-cell 加载项怎么解决?
  10. 调用微软小冰API,实现批量人脸颜值打分
  11. 包含太多_股票开户必备知识,股票交易费包含哪些?
  12. Office EXCEL 如何保留两位小数,四舍五入
  13. 定义一个复数类Complex,使得下面的代码能够工作:
  14. 以太坊MEV黑暗森林发展史:从Gas战争到PBS
  15. DEBUG指示灯详细说明
  16. java毕业设计校园快递柜存取件系统mybatis+源码+调试部署+系统+数据库+lw
  17. 简单使用Python爬虫爬取淘宝网页商品信息
  18. Django路由写法
  19. win10计算机怎么注销用户,windows10系统如何取消微软账户登陆
  20. 美甲实体行业没有客户?想做线上引流?这些渠道千万别错过!

热门文章

  1. 分区表PARTITION table
  2. Effective C# 原则33:限制类型的访问(译)
  3. 制作了一个WMI 查询分析工具
  4. 最大子矩阵问题悬线法 学习小结
  5. Lazarus 日志工具 MultiLog
  6. 用IDEA开发一个简单的Spring MVC的Hello World程序,表单程序
  7. 如何成为优秀的科学家
  8. Spring MVC 自定义验证器示例
  9. opencms内容管理入门指南pdf_企业微信管理员训练营回顾(三) | 企业微信高效协作入门指南...
  10. arial unicode ms字体_适合海报设计的最佳字体