这是我经过两年的ACM竞赛的所有算法总结,为了实现更高的可读性,我整理了一份word文档放到了网盘上
链接:https://pan.baidu.com/s/1zBpYmrNFXUmlQiKpPjTMSA
提取码:e1dd
目录
最短路
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;//自己到自己的路程为0
else
e[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;
else
e[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;
else
e[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;
else
e[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;
else
dp[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多了一个\n
str[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");
else
printf("先手必胜\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");
else
printf("先手必胜\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");
else
printf("先手必胜\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;
else
p++;
}
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;
else
q++;
}
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]);
else
for(i=n-1;i>=0;i--)
printf("%d",max1[i]);
while(1)
{
if(min1[m-1]==0)
m--;
else
break;
}
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;
else
return 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 10
typedef 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;
}
else
flag=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++];
else
temp[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;
}