两年ACM竞赛的所有算法总结
这是我经过两年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竞赛的所有算法总结相关推荐
- ACM如何入门,ACM竞赛需要学习哪些算法?
#################成绩################## 大一:2017年4月份"玲珑杯"河南工业大学2017年ACM程序设计大赛暨河南高校邀请赛,获得银奖 20 ...
- 【退役贴】再见了ACM,再会了算法竞赛
目录 写在前面 心里话 ACM是什么 回顾省赛&昆明 收获 付出 选择ACM竞赛的原因 对一些ACMer的话 之后的打算 写在前面 心里话 2022ICPC昆明站告一段落,随之结束的是我两年多 ...
- 退役帖:再见ACM/ICPC!再见算法竞赛!
结束啦!手指放在键盘上停了好久,却不知从何谈起,那些一幕一幕,又重新浮现眼前.以前很喜欢看一些ACMer的退役帖,有金牌final的,也有铜铁退役,无关成绩,单纯那些奋斗的过程,努力的开心让我很感动. ...
- 做acm 需要学的算法
做acm 需要学的算法 转一个搞ACM需要的掌握的算法. 要注意,ACM的竞赛性强,因此自己应该和自己的实际应用联系起来. 适合自己的才是好的,有的人不适合搞算法,喜欢系统架构,因此不要看到别人什 ...
- ACM竞赛学习整理--矩阵运算
ACM竞赛学习整理–矩阵运算 了解矩阵类 [任务] 实现矩阵的基本变换 [接口] 结构体:Matrix 成员变量: int n,m 矩阵大小 int a[][] 矩阵内容 重载运算符: +.-.x 成 ...
- ACM竞赛学习整理开篇之01背包问题
ACM竞赛学习整理开篇之01背包问题. 最近,偶然的一次机会让我关注信息奥赛的一些内容.发现其中的内容很有趣,是学习编程的一条很好的路径,又能很好地将数学和编程联系到一起.在csdn里看到了不少同好也 ...
- 河南省第十二届ACM竞赛总结
昨天下午紧张刺激的省ACM竞赛结束了,五个小时的大脑超频运转,五十多个学校的二百三十一支队伍在黄淮学院体育场的激烈角逐,这次比上次的CCPC结果要好,一个多月的辛苦总算没有付诸东流,最终我们队取得铜奖 ...
- 2021年ACM竞赛班训练(六)题解
2021年ACM竞赛班训练(六)题解 ==Problem A 逆元== 题目描述 输入 输出 题目分析 代码 ==Problem B&C 五一假期前最后一题&女神的考验== 题目描述 ...
- ACM 中常用的算法有哪些?
在网上看到别人ACM学习的心得,转载过来,源地址不记得了,当时是百度的.内容如下: 网络上流传的答案有很多,估计提问者也曾经去网上搜过.所以根据自己微薄的经验提点看法. 我ACM初期是训练编码能力,以 ...
最新文章
- linux下的hive命令大全,Hive shell 常用命令
- 数据驱动的未来城市八大趋势
- 【注释规约】规范化的第一步,你类和方法的注释,规范嘛?
- 零基础也能看懂python_零基础也能看懂的Python下载网易云音乐爬虫
- 开源软件公司易犯的 5 大错误,又该如何避免?
- Visual Studio可视化IDE风格主题参照
- docker使用阿里云镜像仓库
- 如何构建自己的Java学习体系?
- PowerPoint 中缺少think-cell 加载项怎么解决?
- 调用微软小冰API,实现批量人脸颜值打分
- 包含太多_股票开户必备知识,股票交易费包含哪些?
- Office EXCEL 如何保留两位小数,四舍五入
- 定义一个复数类Complex,使得下面的代码能够工作:
- 以太坊MEV黑暗森林发展史:从Gas战争到PBS
- DEBUG指示灯详细说明
- java毕业设计校园快递柜存取件系统mybatis+源码+调试部署+系统+数据库+lw
- 简单使用Python爬虫爬取淘宝网页商品信息
- Django路由写法
- win10计算机怎么注销用户,windows10系统如何取消微软账户登陆
- 美甲实体行业没有客户?想做线上引流?这些渠道千万别错过!
热门文章
- 分区表PARTITION table
- Effective C# 原则33:限制类型的访问(译)
- 制作了一个WMI 查询分析工具
- 最大子矩阵问题悬线法 学习小结
- Lazarus 日志工具 MultiLog
- 用IDEA开发一个简单的Spring MVC的Hello World程序,表单程序
- 如何成为优秀的科学家
- Spring MVC 自定义验证器示例
- opencms内容管理入门指南pdf_企业微信管理员训练营回顾(三) | 企业微信高效协作入门指南...
- arial unicode ms字体_适合海报设计的最佳字体