关于dp部分的思考

dp部分小结

背包

背包主要是模型的构建。

01背包

选与不选,且只能选一个。

for(int i=1;i<=n;i++){
    for(int j=mt;j>=w[i];j--)
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}

完全背包

选与不选,可任意选。

for(int i=1;i<=n;i++){
    for(int j=w[i];j<=mt;j++)
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}

多重背包

选与不选,有数目限制。

  • 考虑二进制拆分,讲一个大小为n的物品拆成O(logn)个物品

分组背包

选与不选,每组只能选一个

区间

枚举区间断点

\( dp_{i,j}=max(dp_{i,k}+dp_{k+1,j}) \)

\( dp_{i,j}=max(dp_{i,k-1}+dp_{k+1,j}+c_k) \)

枚举区间最后一个点

数位

比较套路,只要确定好状态和限制就好了

ll dfs(int pos,int num,int lim,int d){
	if(!pos)return num==d;
	if(~dp[pos][num][lim][d])return dp[pos][num][lim][d];
	int ma=lim?bit[pos]:1;
	ll res=0;
	for(int i=0;i<=ma;i++){
		res+=dfs(pos-1,num+(i==1),lim&&i==ma,d);
	}
	return dp[pos][num][lim][d]=res;
}

状压

最好提前预处理合法状态

  • 枚举子集
for(int i=st;i;i=(i-1)&st)
  • 判断是否冲突
if((s1&s2)||(s1&(s2<<1))||((s1<<1)&s2))continue;

环形

树形

换根

斜率优化

一般的,含有转移状态和目标状态有乘积项时,可以考虑斜率优化。

  • $ dp_{i}=dp_{j}+f_{i}+g_{j}+a_{i}*b_{j} $ (只有$ dp_{i} $未知)可化为
  • $ -a_{i}*b_{j}+dp_{i}-f_{i}=dp_{j}+g_{j} $ 的形式
    令$ x=b_{j},k=-a_{i},y=dp_{j}+g_{j},b=dp_{i}-f_{i} $
    原式化为 $ kx+b=y $ 这是我们所熟悉的截距式,我们的目标就是最大(小)化截距
    下面以最小化截距为例
    图片先寄一会儿
    我们发现我们要保留的其实是突出来的的一块,就是凸包。
    这样我们用一个单调队列维护斜率,我们只需要检查队尾是否符合凸包,取队首即为最优。
while(head<tail&&slope(q[head],q[head+1])<k(i)))head++;
while(head<tail&&slope(q[tail-1],i)<slope(q[tail],q[tail-1]))tail--;

ZJOI2007仓库建设
题意很简单,简单说一下解法
很明显我们可以那每个点来做dp状态,记\(dp_{i}\)表示只考虑前i个建设的最小费用
则我们有\(dp_{i}=dp_{j}+c_{i}+\)\(\sum_{k=j+1}^{i}\)\(cost(i,k)*p_{k}\),其中\(cost(i,k)=(x_{i}-x_{j})*p_{j}\)
化简一下
\(dp_{i}=dp_{j}+c_{i}+\)\(x_{i}*\)\(\sum_{k=j+1}^{i}\)\(p_{k}\)+\(\sum_{k=j+1}^{i}\)\(x_{k}*p_{k}\)
\(s_{i}=\)\(\sum_{j=1}^i\)\(x_{j}*p_{j}\),\(sc_{i}=\)\(\sum_{j=1}^i\)\(p_{j}\)
\(dp_{i}=dp_{j}+c_{i}+x_{i}*sp_{i}-x_{i}*sp_{j}-s_{i}+s_{j}\)
化成斜率的形式\(dp_{i}-c_{i}-x_{i}*sp_{i}+s_{i}+x_{i}*sp_{j}=dp_{j}+s_{j}\)
然后就可以斜率优化了
PS:本题hack有点强,要考虑斜率分母为零和后面无效仓库的情况,注意特殊处理,斜率计算的顺序尤为重要

#include<bits/stdc++.h>
#define N 1000400
#define max(x,y) (x>y?x:y)
#define min(x,y) (x<y?x:y)
#define ll long long
#define db double
ll n,m,dp[N],c[N],s[N],sc[N],x[N],p[N],a,b,d;
ll l,r,q[N];
db slope(int i,int j){
	return sc[i]==sc[j]?1e18:1.0*(dp[i]+s[i]-dp[j]-s[j])/(sc[i]-sc[j]);
}
void init(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&a,&b,&d);
		sc[i]=b;s[i]=a*b;c[i]=d;x[i]=a,p[i]=b;
	}
	for(int i=1;i<=n;i++){
		sc[i]+=sc[i-1];
		s[i]+=s[i-1];
	}
}
void solve(){
	l=r=1;q[1]=0;
	for(int i=1;i<=n;i++){
		while(l<r&&slope(q[l+1],q[l])<1.0*x[i])l++;
		dp[i]=dp[q[l]]+c[i]+x[i]*(sc[i]-sc[q[l]])-(s[i]-s[q[l]]);
		while(l<r&&slope(q[r],q[r-1])>slope(i,q[r]))r--;
		q[++r]=i;
	}
	ll ans=1e18,lis=n;
	while(!p[lis]&&lis)lis--;
	if(!lis){
		puts("0");return;
	}
	for(int i=lis;i<=n;i++)ans=min(ans,dp[i]);
	printf("%lld\n",ans);
}
int main(){
	init();
	solve();
	return 0;
}
posted @ 2023-06-23 08:50  EnderWave  阅读(40)  评论(0)    收藏  举报