線性DP&揹包DP
阿新 • • 發佈:2022-01-25
具有線性規劃特點的DP型別稱為線性DP
這類DP一般是較為基礎的蒟蒻不提簡單二字
DP:
狀態表示應滿足三個特點:
1.最優化:滿足最優子結構性質
(略微不同於貪心的“滾雪球”,DP演算法不一定滿足區域性最優導致全域性最優,但DP演算法可以通過更新最優解實現全域性最優)
2.無後效性:即當前問題的決策不受後續決策的影響,這就是無後效性
比方說大佬翻身成為蒟蒻
3.子問題的重複性:應用DP的前提是子問題的相似性,這使得我們可以遞推出來實現最優解的關係式:狀態轉移方程
所以,狀態轉移方程是DP演算法實現的關鍵
線性DP:
線性DP的狀態轉移沿著維度有方向增長
下面DP典例:
1.LIS(最長上升子序列):顯然如果有i,j使得i>j&&Ai>Aj,
那麼Ai便可以新增到以Aj為結尾的上升子序列中,
與原以Ai結尾的上升子序列相比可得長度更長的更優,也就是狀態更新
那麼通過如上分析便可得到狀態轉移方程:
Bi=max(Bi,Bj+1),特殊的,B0=0
2.LCS(最長公共子序列)
這裡我們用已處理過的部分和未處理過的部分相劃分開
那麼狀態轉移方程為:
F【i,j】=max(F【i-1,j】,F【i,j-1】,F【i-1,j-1】+1)
揹包DP:原本揹包9講變7講,我再吞3講也沒事吧
首先對於所有的揹包DP來說,它們的狀態轉移方程統一,為:
F【j】=max(F【j】,F【j-v【i】】+w【i】)
其中i表示第i種物品對應價值w【i】,價格v【i】,j表示包內重量為j時所帶來的最大價值為F【j】(上述價格與重量均表示為“代價”)
註釋:在上述狀態轉移方程中:f【j-v【i】】毫無疑問是在包內質量位於j-v【i】時所獲得最大值的狀態,
此狀態下加上w【i】表示假設如果在此狀態下加上w【i】的值(顯然w【i】對前一部分無影響,仍是理想下的最優狀態),
與當前的最優狀態比較,完成最大值的更新,最優狀態的轉移完成
1.0/1DP
N種物品,每種物品僅有1個,價值為C,價格為V,你所能付出的最大代價為M,
試求出不超過承受最大代價時所能收穫的最大價值
套上述狀態轉移方程即可
Code
點選檢視程式碼
void DP(){ for(int i=1;i<=n;i++){//從第1種物品開始列舉到第n種物品 for(int j=m;j>=v[i];j--){//0/1揹包倒序查詢防止出現某種物品重複使用 f[j]=max(f[j],f[j-v[i]]+c[i]);//狀態轉移方程 } } for(int i=1;i<=m;i++){//查詢各種代價所得到的最優解 ans=max(ans,f[i]);//用最優解更新答案 } cout<<ans;//輸出答案 return;//結束,會了 }
點選檢視程式碼
void DP(){
for(int i=1;i<=n;i++){//從1到n遍歷物品
for(int j=v[i];j<=m;j++){//不同於0/1,這裡正序迴圈保證可以使用無限件物品,當j的值從Ma變到Ma+v【i】時還可以使用i物品
f[j]=max(f[j],f[j-v[i]]+c[j]);//狀態轉移更新
}
}
for(int i=1;i<=m;i++){//遍歷各種代價下的最優解
ans=max(ans,f[i]);//更新答案
}
cout<<ans;//輸出結果
return;//結束
}
優化演算法
#include <bits/stdc++.h>//單調佇列維護多重揹包
using namespace std;
int n,m,ans;
int v[501],w[501],c[501],f[6000];
int q[10000];
int calc(int i,int u,int k){
return f[u+k*v[i]]-k*w[i];
}
int main(){
cin>>n>>m;
memset(f,0xcf,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v[i],&w[i],&c[i]);
for(int u=0;u<v[i];u++){
int l=1,r=0;
int maxp=(m-u)/v[i];
for(int k=maxp-1;k>=max(maxp-c[i],0);k--){
while(l<=r&&calc(i,u,q[r])<=calc(i,u,k))r--;
q[++r]=k;
}
for(int p=maxp;p>=0;p--){
while(l<=r&&q[l]>p-1)l++;
if(l<=r)
f[u+p*v[i]]=max(f[u+p*v[i]],calc(i,u,q[l])+p*w[i]);
if(p-c[i]-1>=0){
while(l<=r&&calc(i,u,q[r])<=calc(i,u,p-c[i]-1))r--;
q[++r]=p-c[i]-1;
}
}
}
}
for(int i=1;i<=m;i++){
ans=max(ans,f[i]);
}
cout<<ans;
return 0;
}
Code
memset(f,0xcf,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--)
for(int k=1;k<=c[i];k++)
for(int k=1;k<=c[i];k++)
if(j>=v[i][k])
f[j]=max(f[j],f[j-v[i][k]]+c[i][k]);