核心算法ACM模板.docx
《核心算法ACM模板.docx》由会员分享,可在线阅读,更多相关《核心算法ACM模板.docx(25页珍藏版)》请在冰豆网上搜索。
![核心算法ACM模板.docx](https://file1.bdocx.com/fileroot1/2022-10/17/75701aa6-2305-4a78-b288-1e6c170e57dd/75701aa6-2305-4a78-b288-1e6c170e57dd1.gif)
核心算法ACM模板
一、 贪心算法 1
1、 区间选点 1
2、 区间覆盖 1
3、 不相交区间 2
4、 哈夫曼编码 2
5、 最小值最大化、最大值最小化(二分查找) 2
二、 动态规划 7
1、 最长公共子序列(LCS) 7
2、 最长上升公共子序列(LIS) 9
3、 子段和 13
4、 DAG上的动态规划 19
5、 区间DP 25
6、 状态压缩DP 35
7、 双线DP 38
8、 背包问题(见背包九讲) 41
三、 数据结构 41
1、 并查集 41
2、 树状数组 43
3、 (字符串)KMP匹配 45
四、 最小生成树算法 48
Prime核心算法 48
Kruskal算法 53
五、 单源最短路径 57
Dijkstra核心算法 57
Bellman_Ford算法 61
SPFA算法(Bellman_Ford的队列实现) 64
六、 二分图匹配 67
1、 匈牙利算法 67
七、 网络流 68
1、 SAP算法 68
2、 Dinic算法 70
一、贪心算法
1、区间问题
区间选点
选取尽量少的点覆盖所有的区间,是每个区间至少包含一个点。
对区间右端点进行排序。
区间覆盖
选取尽量少的区间覆盖整个区域。
对左端点进行排序。
不相交区间
选取尽量多的不相交区间。
对区间右端点进行排序。
2、哈夫曼编码
3、最小值最大化、最大值最小化(二分查找)
NYOJ疯牛问题(最小值最大化)
农夫John建造了一座很长的畜栏,它包括N(2<=N<=100,000)个隔间,这些小隔间依次编号为x1,...,xN(0<=xi<=1,000,000,000).
但是,John的C(2<=C<=N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。
为了不让牛互相伤害。
John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
#include
#include
#include
usingnamespacestd;
intn,c;
intpos[100005];
booljudge(intk)
{
intcnt=1;
intst=pos[0];
for(inti=1;i{
if(pos[i]-st>=k)
{
++cnt;
if(cnt>=c)
returntrue;
st=pos[i];
}
}
returnfalse;
}
intBinary_search(intleft,intright) ///二分枚举满足条件的最大距离
{
while(left<=right)
{
intmid=(left+right)>>1;
if(judge(mid)) ///所求距离>=mid,可以继续增大试探
left=mid+1;
else ///所求距离right=mid-1;
}
returnleft-1;
}
intmain()
{
while(~scanf("%d%d",&n,&c))
{
for(inti=0;iscanf("%d",&pos[i]);
sort(pos,pos+n);
printf("%d\n",Binary_search(0,pos[n-1]-pos[0]));
}
return0;
}
NYOJ摘枇杷(最大值最小化)
理工学院的枇杷快熟了,ok,大家都懂得。
而且大家都知道,学校的枇杷树都是一列一列的。
现在小Y同学已经在筹划怎么摘枇杷了。
现在我们假设有一列枇杷树,而且每棵枇杷树上枇杷果的数量小Y都已经知道了。
假设现在有n棵枇杷树,小Y可以把这n棵枇杷树分成m组,每组枇杷果的数量是这组内每棵枇杷树上枇杷果数量的和。
注意,每组的枇杷树必须是连续的。
(每组最少1棵树,最多n棵树)。
小Y把枇杷往寝室拿的时候是一组一组拿的,所花费的力气等于这m组中枇杷果最多的那组枇杷果的数量。
现在小Y想花尽量少的力气把这些枇杷果拿回寝室。
#include
#include
intn,m,a[1005];
booljudge(intx)
{
ints=0,count=0;
for(inti=0;i{
if(s+a[i]>x)
{
count++;
s=a[i];
if(count>m-1)///当count==m时,而ireturnfalse;
}
else
s+=a[i];
}
returntrue; ///每次运送x时,m组内就能运完
}
intBinary_search(intl,intr)
{
while(l<=r)
{
intmid=(l+r)/2;
if(judge(mid))r=mid-1;
elsel=mid+1;
}
returnl; ///当从while中退出时,最后一次操作是r=mid-1,原本的l<=r不再满足,所以l才是所要求的值
}
intmain()
{
while(~scanf("%d%d",&n,&m))
{
memset(a,0,sizeof(a));
intmax=0,sum=0;
for(inti=0;i{
scanf("%d",&a[i]);
sum+=a[i];
max=max>a[i]?
max:
a[i];
}
printf("%d\n",Binary_search(max,sum));
}
return0;
}
二、动态规划
最长公共子序列(LCS)
最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(LongestCommonSubsequence)。
其定义是,一个序列S,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则S称为已知序列的最长公共子序列。
二维表:
for(inti=1;i<=n1;i++)
{
for(intj=1;j<=n2;j++)
{
if(a[i]==b[j])c[i][j]=c[i-1][j-1]+1;
elseif(c[i-1][j]>=c[i][j-1])c[i][j]=c[i-1][j];
elsec[i][j]=c[i][j-1];
}
}
空间优化(滚动数组)
#include
#include
intdp[1005];
intmain()
{
intt;
scanf("%d",&t);
while(t--)
{
getchar();
chara[1005]={0},b[1005]={0};
memset(dp,0,sizeof(dp));
scanf("%s%s",a+1,b+1);
intla=strlen(a+1);
intlb=strlen(b+1);
for(inti=1;i<=la;i++)
{
intold=0;//old表示c[i-1][j-1]
for(intj=1;j<=lb;j++)
{
inttmp=dp[j]; //注意
if(a[i]==b[j])dp[j]=old+1;
elseif(dp[j-1]>dp[j])dp[j]=dp[j-1];
old=tmp;
}
}
printf("%d\n",dp[lb]);
}
return0;
}
时间优化(转化为LIS时间复杂度O(nlogn))见LIS
1、最长上升公共子序列(LIS)
最长递增子序列问题:
在一列数中寻找一些数,这些数满足:
任意两个数a[i]和a[j],若i设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为:
dp[i]=max{dp[j]+1},1<=j
这样简单的复杂度为O(n^2),其实还有更好的方法。
考虑两个数a[x]和a[y],x显然选取a[x]更有潜力,因为可能存在a[x]在这里给我们一个启示,当dp[t]一样时,尽量选择更小的a[x].
按dp[t]=k来分类,只需保留dp[t]=k的所有a[t]中的最小值,设d[k]记录这个值,d[k]=min{a[t],dp[t]=k}。
这时注意到d的两个特点(重要):
1.d[k]在计算过程中单调不升;
2.d数组是有序的,d[1]利用这两个性质,可以很方便的求解:
1.设当前已求出的最长上升子序列的长度为len(初始时为1),每次读入一个新元素x:
2.若x>d[len],则直接加入到d的末尾,且len++;(利用性质2)
否则,在d中二分查找,找到第一个比x小的数d[k],并d[k+1]=x,在这里x<=d[k+1]一定成立(性质1,2)。
动态规划(O(n^2))
#include
#include
ints[100005]={0};
intdp[100005]={0};
intmain()
{
intn;
while(~scanf("%d",&n)&&n!
=EOF)
{
intmax=0;
for(inti=0;i{
scanf("%d",&s[i]);
dp[i]=0;
for(intj=0;j
if(s[j]
dp[i]++;
if(max}
printf("%d\n",max);
}
return0;
}
动态规划+二分查找(O(nlogn))
#include
#include
intdp[100010];
intBinarysearch(intkey,intlen)
{
intleft=0,right=len-1,mid;
while(left<=right)
{
mid=(left+right)/2;
if(key==dp[mid])
returnmid;
elseif(key>dp[mid])
left=mid+1;
else
right=mid-1;
}
returnleft;