整理的算法模板合集: ACM模板


目录

  • 一:【背包】
  • 二:【树形DP】
  • 三:【数位DP】
  • 四:【DP 的优化】
  • 五:【插头 DP】

一:【背包】

1.【01背包】

#include<algorithm>
#include<cstdio>
int T,n,i,j,v[110],w[110],f[1010];
int main(){scanf("%d%d",&T,&n);for(i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);for(i=1;i<=n;i++)for(j=T;j>=v[i];j--)f[j]=std::max(f[j],f[j-v[i]]+w[i]);printf("%d",f[T]);
}

2.【完全背包】

#include<bits/stdc++.h>
using namespace std;
int T,n,ans,a[105],dp[30005];
int main(){scanf("%d",&T);while(T--){memset(dp,-127,sizeof(dp));scanf("%d",&n);dp[0]=ans=0;for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)for(int j=a[i];j<=25000;j++)dp[j]=max(dp[j],dp[j-a[i]]+1);for(int i=1;i<=n;++i)ans+=dp[a[i]]==1;printf("%d\n",ans);}
}

3.【多人背包】

他们一共有 K 个人,每个人都会背一个包。这些包 的容量是相同的,都是 V。可以装进背包里的一共有 N 种物品,每种物品都有
给定的体积和价值。 在 DD 看来,合理的背包安排方案是这样的: 每个人背包里装的物品的总体积恰等于包的容量。
每个包里的每种物品最多只有一件,但两个不同的包中可以存在相同的物品。 任意两个人,他们包里的物品清单不能完全相同。
在满足以上要求的前提下,所有包里的所有物品的总价值最大是多少呢?

#include<algorithm>
#include<cstring>
#include<cstdio>
int ans,T,n,i,j,k,K,a,b,t,re[45],v[210],w[210],f[5010][45];
int main(){scanf("%d%d%d",&K,&T,&n);memset(f,-127,sizeof(f));f[0][1]=0;for(i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);for(i=1;i<=n;i++)for(j=T;j>=v[i];j--){a=b=1,t=0;while(t<=K)if(f[j][a]>=f[j-v[i]][b]+w[i])re[++t]=f[j][a++];else re[++t]=f[j-v[i]][b++]+w[i];for(k=1;k<=K;k++)f[j][k]=re[k];}for(i=1;i<=K;i++)ans+=f[T][i];printf("%d",ans);
}

4.【分组背包】

#include<algorithm>
#include<cstdio>
using namespace std;
int T,n,i,j,a[65],s,t,r1,r2,r[65][3],v[65],w[65],f[32010];
int main(){scanf("%d%d",&T,&n);v[0]=0xfffffff;for(i=1;i<=n;i++){scanf("%d%d%d",&v[i],&s,&a[i]);if(a[i])r[a[i]][++r[a[i]][0]]=i;w[i]=v[i]*s;}for(i=1;i<=n;i++)for(j=T;j>=v[i]&&(!a[i]);j--){t=j-v[i],r1=r[i][1],r2=r[i][2];f[j]=max(f[j],f[t]+w[i]);if(v[r1]<=t)f[j]=max(f[j],f[t-v[r1]]+w[i]+w[r1]);if(v[r2]<=t)f[j]=max(f[j],f[t-v[r2]]+w[i]+w[r2]);if(v[r1]+v[r2]<=t)f[j]=max(f[j],f[t-v[r1]-v[r2]]+w[i]+w[r1]+w[r2]);}printf("%d",f[T]);
}

5.【多重背包】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
using namespace std;
const int N=103,M=4e4+3;
int n,m,x,y,z,V,ans,v[N*17],w[N*17],dp[M];
inline void in(Re &x){Re fu=0;x=0;char ch=getchar();while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();x=fu?-x:x;
}
int main(){in(m),in(V);memset(dp,-127,sizeof(dp));dp[0]=0;while(m--){in(x),in(y),in(z);Re p=1;while(z>=p)v[++n]=y*p,w[n]=x*p,z-=p,p<<=1;if(z)v[++n]=y*z,w[n]=x*z;}for(Re i=1;i<=n;++i)for(Re j=V;j>=v[i];--j)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);for(Re i=0;i<=V;++i)ans=max(ans,dp[i]);printf("%d\n",ans);
}

6.【混合背包】

#include<algorithm>
#include<cstdio>
using namespace std;
int x,a,b,c,d,T,n,i,j,t,f[1010],v[100010],w[100010];
int main(){scanf("%d:%d%d:%d%d",&a,&b,&c,&d,&n);T=c*60+d-a*60-b;for(i=1;i<=n;i++){scanf("%d%d%d",&a,&b,&c);if(!c){for(j=0;j+a<=T;j++)f[j+a]=max(f[j+a],f[j]+b);continue;}x=1;while(c>=x){v[++t]=x*a,w[t]=x*b,c-=x,x<<=1;}v[++t]=c*a,w[t]=c*b;}for(i=1;i<=t;i++)for(j=T;j>=v[i];j--)f[j]=max(f[j],f[j-v[i]]+w[i]);printf("%d",f[T]);
}

7.【二维费用】

#include<algorithm>
#include<cstdio>
using namespace std;
int tmp,T1,T2,x,y,n,i,j,k,v1[105],v2[105],w[105],dp[105][105],ans[105][105];
int main(){scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d%d%d",&v1[i],&v2[i],&w[i]);scanf("%d%d",&T1,&T2);for(i=1;i<=n;++i)for(j=T1;j>=v1[i];--j)for(k=T2;k>=v2[i];--k)if(dp[j][k]<(tmp=dp[j-v1[i]][k-v2[i]]+1)){dp[j][k]=tmp;ans[j][k]=ans[j-v1[i]][k-v2[i]]+w[i];}else if(dp[j][k]==tmp)ans[j][k]=min(ans[j][k],ans[j-v1[i]][k-v2[i]]+w[i]);printf("%d",ans[T1][T2]);
}

二:【树形DP】

(1).【简单树形 DP】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define R register int
using namespace std;
int head[6010],a,b,t,i,j,n,f[6010][2],pan[6010];
struct QAQ{int next,to;}Q[6010];
inline void add(int x,int y){Q[++t].to=y,Q[t].next=head[x],head[x]=t;}
inline void dfs(int w){for(R k=head[w];k;k=Q[k].next){int to=Q[k].to;dfs(to);f[w][0]+=max(f[to][0],f[to][1]);f[w][1]+=f[to][0];}
}
int main(){scanf("%d",&n);for(i=1;i<=n;i++)scanf("%d",&f[i][1]);for(i=1;i<n;i++)scanf("%d%d",&a,&b),pan[a]++,add(b,a);for(i=1;i<=n;i++)if(!pan[i]){dfs(i);printf("%d\n",max(f[i][0],f[i][1]));return 0;}
}

(2).【换根 DP】

每个奶牛居住在 N 个农场中的一个,这些农场由 N−1 条道路连接,并且从任意一个农场都能够到达另外一个农场。道路 i 连接农场
AiA_iAi​和BiB_iBi​,长度为 LiL_iLi​。集会可以在 N 个农场中的任意一个举行。另外,每个牛棚中居住着 CiC_iCi​只奶牛。
在选择集会的地点的时候,Bessie 希望最大化方便的程度(也就是最小化不方便程度)。比如选择第 X
个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和(比如,农场 i 到达农场 X 的距离是 20,那么总路程就是
Ci×20C_i\times 20Ci​×20。帮助 Bessie 找出最方便的地点来举行大集会。

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const LL N=1e5+3,inf=1e18;
int n,m,x,y,z,o,A[N],S[N],dis[N],size[N],head[N];LL ans=inf,f[N],g[N];
struct QAQ{int w,to,next;}a[N<<1];
inline void add(Re x,Re y,Re z){a[++o].w=z,a[o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
inline void dfs1(Re x,Re fa){size[x]=1,S[x]=A[x];for(Re i=head[x],to;i;i=a[i].next)if((to=a[i].to)!=fa)dis[to]=a[i].w,dfs1(to,x),S[x]+=S[to],size[x]+=size[to],f[x]+=f[to]+(LL)S[to]*a[i].w;
}
inline void dfs2(Re x,Re fa){if(fa)g[x]=g[fa]+f[fa]-f[x]-(LL)S[x]*dis[x]+(LL)(S[1]-S[x])*dis[x];for(Re i=head[x],to;i;i=a[i].next)if((to=a[i].to)!=fa)dfs2(to,x);ans=min(ans,f[x]+g[x]);
}
int main(){//    freopen("123.txt","r",stdin);in(n),m=n-1;for(Re i=1;i<=n;++i)in(A[i]);while(m--)in(x),in(y),in(z),add(x,y,z),add(y,x,z);dfs1(1,0),dfs2(1,0),printf("%lld\n",ans);
}

三:【数位DP】

不含前导零且相邻两个数字之差至少为 2 的正整数被称为 windy 数。windy 想知道,在 a 和 b 之间,包括 a 和 b ,总共有多少个 windy 数?

1.【dfs】

#include<cstring>
#include<cstdio>
#include<cmath>
#define R register int
using namespace std;
int a,b,l,num[12],dp[12][10];
inline int dfs(int len,int up,int ok){R i,ed,ans=0;if(len<1)return 1;if(!ok&&~dp[len][up]&&up!=-7)return dp[len][up];ed=ok?num[len]:9;for(i=0;i<=ed;i++)if(abs(i-up)>=2)ans+=dfs(len-1,(!i&&up==-7)?up:i,ok&&(i==ed));if(!ok&&up!=-7)dp[len][up]=ans;return ans;
}
inline int sovle(int x){l=0;while(x)num[++l]=x%10,x/=10;return dfs(l,-7,1);
}
int main(){scanf("%d%d",&a,&b);memset(dp,-1,sizeof(dp));printf("%d",sovle(b)-sovle(a-1));
}

2.【dp】

#include<cstdio>
#include<cmath>
#define R register int
using namespace std;
int i,j,k,a,b,l,ans,num[12],dp[12][10];
inline int dpp(int len){ans=0;for(i=1;i<len;i++)for(j=1;j<10;j++)ans+=dp[i][j];for(j=1;j<num[len];j++)ans+=dp[len][j];for(i=len-1;i>0;i--){for(j=0;j<num[i];j++)if(abs(num[i+1]-j)>=2)ans+=dp[i][j];if(abs(num[i]-num[i+1])<2)break;}if(!i)ans++;return ans;
}
inline int sovle(int x){l=0;while(x)num[++l]=x%10,x/=10;return dpp(l);
}
inline int sakura(){scanf("%d%d",&a,&b);for(i=0;i<10;i++)dp[1][i]=1;for(i=2;i<11;i++)for(j=0;j<10;j++)for(k=0;k<10;k++)if(abs(j-k)>=2)dp[i][j]+=dp[i-1][k];printf("%d",sovle(b)-sovle(a-1));
}
int QAQWQ=sakura();
int main(){}

四:【DP 的优化】

1.【二分】
最长公共子序列

#include<bits/stdc++.h>
using namespace std;
int pan[100010],b[100010],f[100010],n,i,a,len=1,l,r,mid;
inline int in(){int x=0,fh=1;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x*fh;
}
int main(){n=in();for(i=1;i<=n;i++)a=in(),pan[a]=i,f[i]=0xfffffff;for(i=1;i<=n;i++)a=in(),b[i]=pan[a];f[1]=b[1];for(i=2;i<=n;i++){l=0;r=len;if(b[i]>f[len])f[++len]=b[i];else{while(l<r){mid=(l+r)/2;if(f[mid]<b[i])l=mid+1;else r=mid;}f[l]=min(b[i],f[l]);}}printf("%d",len);
}

2.【二进制优化多重背包】

#include<algorithm>
#include<cstdio>
#define Re register int
using namespace std;
const int N=103,M=4e4+3,logN=17;
int m,n,x,y,z,V,ans,v[N*logN],w[N*logN],dp[M];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
int main(){in(m),in(V);for(Re i=1;i<=m;++i){in(x),in(y),in(z);for(Re j=1;j<=z;j<<=1)z-=j,w[++n]=x*j,v[n]=y*j;if(z)w[++n]=x*z,v[n]=y*z;}for(Re i=1;i<=n;++i)for(Re j=V;j>=v[i];--j)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);for(Re j=0;j<=V;++j)ans=max(ans,dp[j]);printf("%d\n",ans);
}

3.【单调队列优化多重背包】

#include<cstdio>
#define Re register int
const int N=7003,M=7003;
int n,h,t,V,mp,tmp,v[N],w[N],c[N],Q[N],K[N],dp[M];
inline void in(Re &x){Re fu=0;x=0;char ch=getchar();while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();x=fu?-x:x;
}
inline int max(Re a,Re b){return a>b?a:b;}
inline int min(Re a,Re b){return a<b?a:b;}
int main(){in(n),in(V);for(Re i=1;i<=n;++i)in(v[i]),in(w[i]),in(c[i]);for(Re i=1;i<=n;++i)for(Re r=0;r<v[i];++r){h=1,t=0,mp=(V-r)/v[i];for(Re p=0;p<=mp;++p){tmp=dp[p*v[i]+r]-w[i]*p;while(h<=t&&Q[t]<=tmp)--t;Q[++t]=tmp,K[t]=p;while(h<=t&&p-K[h]>min(c[i],V/v[i]))++h;dp[p*v[i]+r]=max(dp[p*v[i]+r],Q[h]+p*w[i]);}}printf("%d",dp[V]);
}

4.【矩阵加速递推】
【学习笔记】动态规划—矩阵递推加速
5.【四边形不等式优化】
【模板】
(1).【分治】

#include<algorithm>
#include<cstdio>
#include<cmath>
#define Re register int
using namespace std;
const int N=5e5+3;
int i,j,n,h,t,a[N],Q[N],Poi[N];
double tmp,p[N],sqr[N];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
inline void sakura(Re l,Re r,Re L,Re R){if(l>r)return;Re mid=l+r>>1,j0;double mx=0;for(Re j=L;j<=mid&&j<=R;++j)
//暴力找p[i]的最优决策点j0,而其决策点j必须满足j<=i,即此处的j<=midif((tmp=a[j]+sqr[mid-j])>mx)mx=tmp,j0=j;p[mid]=max(p[mid],mx);sakura(l,mid-1,L,j0),sakura(mid+1,r,j0,R);
}
int main(){in(n);for(Re i=1;i<=n;++i)in(a[i]),sqr[i]=sqrt(i);sakura(1,n,1,n);for(Re i=1;i<n-i+1;++i)swap(a[i],a[n-i+1]),swap(p[i],p[n-i+1]);sakura(1,n,1,n);for(Re i=n;i;--i)printf("%d\n",(int)ceil(p[i])-a[i]);
}

(2).【二分栈】

#include<algorithm>
#include<cstdio>
#include<cmath>
#define Re register int
using namespace std;
const int N=5e5+3;
int i,j,n,h,t,a[N],Q[N],Poi[N];
double p[N],sqr[N];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
inline double Y(Re i,Re j){return a[j]+sqr[i-j];}
inline int find_Poi(int j1,int j2){//找到两个直线的交点iRe l=j2,r=n,mid,ans=n+1;//为了处理两个直线没有交点的情况,用一个变量记录答案while(l<=r){mid=l+r>>1;if(Y(mid,j1)<=Y(mid,j2))ans=mid,r=mid-1;
//当前这个位置i使得直线j1的纵坐标小于直线j2的纵坐标,说明这个点i在交点的右方,所以右边界要缩小else l=mid+1;}return ans;
}
inline void sakura(){h=1,t=0;for(i=1;i<=n;++i){//由于i本身也是一个决策点,所以要先入队再取答案择优while(h<t&&Poi[t-1]>=find_Poi(Q[t],i))--t;//如果出现了上述可踢的情况,出队Poi[t]=find_Poi(Q[t],i),Q[++t]=i;while(h<t&&Poi[h]<=i)++h;
//找到第一个位置j使得直线Q[j]与直线Q[j+1]的交点大于i,
//那么直线Q[j]就是i前面在最上面的直线,即答案,自己画个图模拟一下就懂了p[i]=max(p[i],Y(i,Q[h]));}
}
int main(){in(n);for(Re i=1;i<=n;++i)in(a[i]),sqr[i]=sqrt(i);sakura();for(Re i=1;i<n-i+1;++i)swap(a[i],a[n-i+1]),swap(p[i],p[n-i+1]);sakura();for(Re i=n;i;--i)printf("%d\n",(int)ceil(p[i])-a[i]);
}

6.【斜率优化】

(1).【单调队列(x 与 k 均单调)】

#include<cstring>
#include<cstdio>
#define LL long long
#define Re register LL
const int N=5e4+5;
LL i,j,n,L,h=1,t=0,Q[N],S[N],dp[N];
//S[n]=∑C[i]+1, dp[i]=min(dp[j]+(S[i]-(S[j]+L+1))^2),++L
//dp[i]=S[i]^2-2*S[i]*L+dp[j]+(S[j]+L)^2-2S[i]*S[j]
//(2*S[i]) * S[j] + (dp[i]-S[i]^2+2S[i]L)=(dp[j]+(S[j]+L)^2)
//   k     *  x   +           b          =        y
inline LL min(Re a,Re b){return a<b?a:b;}
inline LL X(Re j){return S[j];}
inline LL Y(Re j){return dp[j]+(S[j]+L)*(S[j]+L);}
inline long double slope(Re i,Re j){return (long double)(Y(j)-Y(i))/(X(j)-X(i));}//记得开long double
int main(){scanf("%lld%lld",&n,&L);++L;for(i=1;i<=n;S[i]+=S[i-1]+1,++i)scanf("%lld",&S[i]);Q[++t]=0;//重中之重for(i=1;i<=n;++i){while(h<t&&slope(Q[h],Q[h+1])<=2*S[i])++h;//至少要有两个元素 h<t。出队判断时尽量加上等号dp[i]=dp[j=Q[h]]+(S[i]-S[j]-L)*(S[i]-S[j]-L);while(h<t&&slope(Q[t-1],Q[t])>=slope(Q[t-1],i))--t;//至少要有两个元素 h<t。入队判断时尽量加上等号Q[++t]=i;}printf("%lld",dp[n]);
}

(2).【二分+单调队列(x 单调 k 不单调)】

#include<cstring>
#include<cstdio>
#define LL long long
#define Re register LL
const int N=3e5+5;
LL i,j,n,h=1,t=0,S,Q[N],ST[N],SF[N],dp[N];
//dp[p][i]=min(dp[p-1][j]+(ST[i]+S*p)*(SF[i]-SF[j]));
//dp[i]=dp[j]+ST[i]*(SF[i]-SF[j])+S*(SF[n]-SF[j]);
//(S+ST[i]) * SF[j] + (dp[i]-ST[i]*SF[i]-S*SF[i]) = (dp[j])
//    k     *   x   +              b              = y
//ti可小于0,所以ST[i]非递增,只可二分
//fi可等于0,所以SF[i](X)非严格递增,因此需要特判X(i)==X(j)的情况
inline LL min(Re a,Re b){return a<b?a:b;}
inline LL X(Re j){return SF[j];}
inline LL Y(Re j){return dp[j];}
inline long double slope(Re i,Re j){return X(j)==X(i)?(Y(j)>=Y(i)?1e18:-1e18):(long double)(Y(j)-Y(i))/(X(j)-X(i));}
//由于需要二分查找,多了一些限制:队列里不能有在同一位置的点,返回inf还是-inf都影响着是否删除重点,平时不可不管,二分必须注意返回值
inline LL sakura(Re k){if(h==t)return Q[h];Re l=h,r=t;while(l<r){Re mid=l+r>>1,i=Q[mid],j=Q[mid+1];if(slope(i,j)<k)l=mid+1;
//        if( (Y(j) - Y(i)) < k * (X(j) - X(i)) )l=mid+1;//注意是(j)-(i)因为Q[mid+1]>Q[mid]s即j>i即SF[j]>SF[i]即X(j)>X(i),如果是(i)-(j)的话乘过去要变号else r=mid;}return Q[l];
}
int main(){scanf("%lld%lld",&n,&S);for(i=1;i<=n;ST[i]+=ST[i-1],SF[i]+=SF[i-1],++i)scanf("%lld%lld",&ST[i],&SF[i]);Q[++t]=0;for(i=1;i<=n;++i){j=sakura(S+ST[i]);dp[i]=dp[j]+ST[i]*(SF[i]-SF[j])+S*(SF[n]-SF[j]);while(h<t&&slope(Q[t-1],Q[t])>=slope(Q[t-1],i))--t;//此处取等号作用出现,如果不取等会WAQ[++t]=i;}printf("%lld",dp[n]);
}

(3).【CDQ(x 与 k 均不单调)】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LD long double
#define LL long long
#define Re register int
#define S2(a) (1ll*(a)*(a))
using namespace std;
const LL N=1e5+3,inf=1e18;
int n,H[N],W[N],Q[N];LL S[N],dp[N];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
//dp[i]=min(dp[i],dp[j]+(H[i]-H[j])*(H[i]-H[j])+S[i-1]-S[j]);
//dp[i]=dp[j]-2*H[i]*H[j]+H[j]*H[j]+H[i]*H[i]+S[i-1]-S[j]
//(2*H[i]) * H[j] + (dp[i]-S[i-1]-H[i]*H[i]) = (dp[j]+H[j]*H[j]-S[j])
//   k     *  x   +            b             =          y
#define X(j) (a[j].x)
#define Y(j) (a[j].y)
struct QAQ{int k,x,id;LL y;inline bool operator<(const QAQ &O)const{return x!=O.x?x<O.x:y<O.y;}
}a[N],b[N];
inline bool cmp(QAQ A,QAQ B){return A.k<B.k;}
inline LD slope(Re i,Re j){return X(i)==X(j)?(Y(j)>Y(i)?inf:-inf):(LD)(Y(j)-Y(i))/(X(j)-X(i));}
inline void CDQ(Re L,Re R){if(L==R){Re j=a[L].id;a[L].y=dp[j]+(LL)H[j]*H[j]-S[j];return;}Re mid=L+R>>1,p1=L,p2=mid+1,h=1,t=0;for(Re i=L;i<=R;++i)a[i].id<=mid?b[p1++]=a[i]:b[p2++]=a[i];for(Re i=L;i<=R;++i)a[i]=b[i];CDQ(L,mid);for(Re i=L;i<=mid;++i){while(h<t&&slope(Q[t-1],Q[t])>=slope(Q[t-1],i))--t;Q[++t]=i;}for(Re i=mid+1,j,id;i<=R;++i){while(h<t&&slope(Q[h],Q[h+1])<=a[i].k)++h;if(h<=t)id=a[i].id,j=Q[h],dp[id]=min(dp[id],a[j].y-(LL)a[i].k*a[j].x+S[id-1]+(LL)H[id]*H[id]);}CDQ(mid+1,R);Re w=L-1;p1=L,p2=mid+1;while(p1<=mid&&p2<=R)b[++w]=a[p1]<a[p2]?a[p1++]:a[p2++];while(p1<=mid)b[++w]=a[p1++];while(p2<=R)b[++w]=a[p2++];for(Re i=L;i<=R;++i)a[i]=b[i];
}
int main(){//    freopen("123.txt","r",stdin);in(n);for(Re i=1;i<=n;++i)in(H[i]);for(Re i=1;i<=n;++i)in(W[i]);for(Re i=1;i<=n;++i)S[i]=S[i-1]+W[i],dp[i]=inf;for(Re i=1;i<=n;++i)a[i].k=(H[i]<<1),a[i].x=H[i],a[i].id=i;sort(a+1,a+n+1,cmp);dp[1]=0,CDQ(1,n);printf("%lld\n",dp[n]);
}

(4).【Splay 维护动态凸包(x 与 k 均不单调)】

五:【插头 DP】

1.【多回路插头 DP】

给出n*m的方格,有些格子不能铺线,其它格子必须铺,可以形成多个闭合回路。问有多少种铺法? 从第2行到第n+1行,每行m个数字(1 or
0),1表铺线,0表不铺线

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=15,M=8192+3;
int n,m,o,V,T,A[N][N];LL dp[2][M];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
int main(){//    freopen("123.txt","r",stdin);in(T);while(T--){in(n),in(m),V=(1<<m+1)-1;for(Re i=1;i<=n;++i)for(Re j=1;j<=m;++j)in(A[i][j]);for(Re j=0;j<=V;++j)dp[0][j]=dp[1][j]=0;dp[o][0]=1;for(Re i=1;i<=n;++i){o^=1;for(Re s=0;s<=V;++s)dp[o][s]=(s&1)?0:dp[o^1][s>>1];//把线拉下来for(Re j=1;j<=m;++j){o^=1;for(Re s=0;s<=V;++s)dp[o][s]=0;for(Re s=0;s<=V;++s)if(dp[o^1][s]){Re L=(s>>j-1)&1,U=(s>>j+1-1)&1;if(A[i][j]){//可以铺线dp[o][s^(1<<j-1)^(1<<j+1-1)]+=dp[o^1][s];//横和竖和左上拐和右下拐if(L!=U)dp[o][s]+=dp[o^1][s];//左下拐和右上拐}else{//不可以铺线if(!L&&!U)dp[o][s]+=dp[o^1][s];//无插头才可以转移}}}}printf("%lld\n",dp[o][0]);}
}

2.【单回路插头 DP】
(1).【 括号表示法】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=14,M=1594323+3;
int n,m,o,V,edx,edy,Mi[N],A[N][N];LL ans,dp[2][M];char s[N];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
int main(){//    freopen("123.txt","r",stdin);in(n),in(m),Mi[0]=1;for(Re i=1;i<=m+1;++i)Mi[i]=Mi[i-1]*3;V=Mi[m+1]-1;for(Re i=1;i<=n;++i){scanf("%s",s+1);for(Re j=1;j<=m;++j)if(!(A[i][j]=(s[j]=='*')))edx=i,edy=j;}
//    printf("edx=%d, edy=%d\n",edx,edy);dp[o][0]=1;for(Re i=1;i<=n;++i){o^=1;for(Re s=0;s<=V;++s)dp[o][s]=s%3?0:dp[o^1][s/3];for(Re j=1;j<=m;++j){o^=1;for(Re s=0;s<=V;++s)dp[o][s]=0;for(Re s=0;s<=V;++s)if(dp[o^1][s]){Re L=s/Mi[j-1]%3,U=s/Mi[j+1-1]%3;if(A[i][j]){if(!L&&!U)dp[o][s]+=dp[o^1][s];}else if(!L&&!U)dp[o][s+Mi[j-1]+2*Mi[j+1-1]]+=dp[o^1][s];else if(!L&&U)dp[o][s]+=dp[o^1][s],dp[o][s+U*Mi[j-1]-U*Mi[j+1-1]]+=dp[o^1][s];else if(L&&!U)dp[o][s]+=dp[o^1][s],dp[o][s-L*Mi[j-1]+L*Mi[j+1-1]]+=dp[o^1][s];else if(L==1&&U==1){Re cnt=1;for(Re k=j+2;k<=m;++k){if(s/Mi[k-1]%3==1)++cnt;else if(s/Mi[k-1]%3==2)--cnt;if(!cnt){dp[o][s-Mi[j-1]-Mi[j+1-1]-Mi[k-1]]+=dp[o^1][s];break;}//2变1}}else if(L==2&&U==2){Re cnt=1;for(Re k=j-1;k>=1;--k){if(s/Mi[k-1]%3==1)--cnt;else if(s/Mi[k-1]%3==2)++cnt;if(!cnt){dp[o][s-2*Mi[j-1]-2*Mi[j+1-1]+Mi[k-1]]+=dp[o^1][s];break;}//1变2}}else if(L==2&&U==1)dp[o][s-2*Mi[j-1]-Mi[j+1-1]]+=dp[o^1][s];else if(L==1&&U==2)if(i==edx&&j==edy&&!(s-Mi[j-1]-2*Mi[j+1-1]))ans+=dp[o^1][s];}}}printf("%lld\n",ans);
}

(2).【括号表示法+优化状态枚举】
【模板】 插头 dp

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=14,M=1594323+3;
int n,m,o,V,edx,edy,Mi[N],A[N][N];LL ans,dp[2][M];char s[N];
inline void in(Re &x){int f=0;x=0;char c=getchar();while(c<'0'||c>'9')f|=c=='-',c=getchar();while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();x=f?-x:x;
}
int st[2][M];
inline void add(Re s,LL v){if(!dp[o][s])st[o][++st[o][0]]=s;dp[o][s]+=v;
}
inline void CL(){for(Re i=1;i<=st[o][0];++i)dp[o][st[o][i]]=0;st[o][0]=0;
}
int main(){//    freopen("123.txt","r",stdin);in(n),in(m),Mi[0]=1;for(Re i=1;i<=m+1;++i)Mi[i]=Mi[i-1]*3;V=Mi[m+1]-1;for(Re i=1;i<=n;++i){scanf("%s",s+1);for(Re j=1;j<=m;++j)if(!(A[i][j]=(s[j]=='*')))edx=i,edy=j;}
//    printf("edx=%d, edy=%d\n",edx,edy);dp[o][st[o][++st[o][0]]=0]=1;for(Re i=1;i<=n;++i){o^=1,CL();for(Re I=1;I<=st[o^1][0];++I)if(st[o^1][I]*3<=V&&dp[o^1][st[o^1][I]])dp[o][st[o][++st[o][0]]=st[o^1][I]*3]=dp[o^1][st[o^1][I]];for(Re j=1;j<=m;++j){o^=1,CL();for(Re I=1;I<=st[o^1][0];++I)if(dp[o^1][st[o^1][I]]){Re s=st[o^1][I];LL v=dp[o^1][s];Re L=s/Mi[j-1]%3,U=s/Mi[j+1-1]%3;if(A[i][j]){if(!L&&!U)add(s,v);}else if(!L&&!U)add(s+Mi[j-1]+2*Mi[j+1-1],v);else if(!L&&U)add(s,v),add(s+U*Mi[j-1]-U*Mi[j+1-1],v);else if(L&&!U)add(s,v),add(s-L*Mi[j-1]+L*Mi[j+1-1],v);else if(L==1&&U==1){Re cnt=1;for(Re k=j+2;k<=m;++k){if(s/Mi[k-1]%3==1)++cnt;else if(s/Mi[k-1]%3==2)--cnt;if(!cnt){add(s-Mi[j-1]-Mi[j+1-1]-Mi[k-1],v);break;}//2变1}}else if(L==2&&U==2){Re cnt=1;for(Re k=j-1;k>=1;--k){if(s/Mi[k-1]%3==1)--cnt;else if(s/Mi[k-1]%3==2)++cnt;if(!cnt){add(s-2*Mi[j-1]-2*Mi[j+1-1]+Mi[k-1],v);break;}//1变2}}else if(L==2&&U==1)add(s-2*Mi[j-1]-Mi[j+1-1],v);else if(L==1&&U==2)if(i==edx&&j==edy&&!(s-Mi[j-1]-2*Mi[j+1-1]))ans+=v;}}}printf("%lld\n",ans);
}

模板 - DP动态规划相关推荐

  1. 算法模板:动态规划之线性DP【沈七】

    算法模板:动态规划之线性DP 前言 线性DP 数字三角形模型 摘花生 最小路径和 不同路径模型 不同路径(有障碍) 过河卒 (综合应用) 最长上升子序列模型 木棍加工 导弹拦截 完结散花 参考文献 前 ...

  2. 【算法模板】动态规划(基础DP篇)

    [算法模板]动态规划(基础DP篇)

  3. DP动态规划企业级模板分析(数字三角,上升序列,背包,状态机,压缩DP)

    前言 经过基础的算法模型讲解和题海战术,将DP动态规划这个重点呢考纲进行细分题类型,此篇是上半章节的DP动态分析提升题.主要包括数字三角形,以及最大上升序列,背包问题,状态机的拆分以及优化状态机的压缩 ...

  4. 算法模板:动态规划之01背包【沈七】

    算法模板:动态规划之01背包 前言 动态规划 01背包 二维背包 一维优化 经典习题 小A点菜 5 倍经验日 买干草 完结散花 参考文献 前言 唤我沈七就好啦. 动态规划 核心作用:优化 当数据范围& ...

  5. (转)dp动态规划分类详解

    dp动态规划分类详解 转自:http://blog.csdn.NET/cc_again/article/details/25866971 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间 ...

  6. python动态规划算法最大k乘积_C语言使用DP动态规划思想解最大K乘积与乘积最大问题...

    最大K乘积问题设I是一个n位十进制整数.如果将I划分为k段,则可得到k个整数.这k个整数的乘积称为I的一个k乘积.试设计一个算法,对于给定的I和k,求出I的最大k乘积. 编程任务: 对于给定的I 和k ...

  7. DP动态规划-爬塔(双层dp)

    DP动态规划-爬塔(双层dp) 比赛来源:牛客 - 中国计量大学现代科技学院第四届"中竞杯"程序设计校赛 题目--------F题 description: 高川最喜欢的游戏当属 ...

  8. DP动态规划--最大子段和问题

    DP–最大子段和问题(动态规划) 问题: 有n个整数组成一个a1,a2,a3,- ,an序列.求其子段和的最大值(子段是连续的) 比如{-2,11,-4,13,-5,-2},最大子段和就是11-4+1 ...

  9. C++入门算法1——dp(动态规划)

    dp(动态规划)是十分重要的一个算法,一般来说这种算法会比dfs(深度优先搜索)快很多. 首先先来看一道例题 题目链接:P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生 ...

最新文章

  1. 禅道 php api,禅道 Rest API 开发
  2. 深圳惠程澄清与奇虎360公司重组传闻
  3. java json 修改字段_JSON文件-Java:编辑/更新字段值
  4. 13.强符号和弱符号
  5. 院士建议:多关注千千万万没有任何“帽子”的青年科技工作者
  6. 易点云在京发布璇玑调度系统 中小企业办公IT升级步伐加快
  7. linux差分包升级流程,OTA 差分升级包的制作
  8. centos7.4下安装mysql_Linux(Centos7.4)服务器下安装mysql
  9. 前景检测算法(四)--GMM
  10. 通过高速计算机网络和多媒体,全国2014.10办公自动化原理及应用试题
  11. 如何在 Linux 中运行.exe 程序
  12. 求推荐民法论文选题?
  13. 数据结构与算法—队列详解
  14. 掌舵9年,艾伦研究所创始CEO 光荣退休!他曾预言中国AI将领跑世界
  15. 无限级分类之递归查找家谱树
  16. mysql查询练习题
  17. android tv 菜单键,Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑
  18. Android,java知识点总结(三)
  19. 如何高效的实现大型设备中卫星信号的传输和分配?
  20. 04---法拉第电磁感应定律

热门文章

  1. 【深度学习】正则化技术全面了解
  2. Batch Normalization的诅咒
  3. 在没有深度学习的情况下找到道路
  4. Kubernetes 1.8.4 手动安装教程-安装Dashboard(七)
  5. python在windows下import其他模块的注意事项
  6. linux 系统性能指标采样脚本
  7. 十八、oracle 角色
  8. VC++6.0如何删除文件
  9. 参加LinuxCon Japan 2012
  10. 企业创新系列之:青苹之末