关于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;
}

浙公网安备 33010602011771号