1. 程式人生 > 其它 >線性DP&揹包DP

線性DP&揹包DP

具有線性規劃特點的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;//結束,會了
}
2.完全揹包 N種物品,每種無數個,價值C,價格V,最大承受代價M 仍問可承受範圍內最大價值 還是一套狀態轉移方程,只不過迴圈結構適當變化 Code
點選檢視程式碼
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;//結束
}
3.多重揹包 N種物品,每種P個,價值C,價格V,最大承受M 我想說的是這裡也有O(MN)演算法
優化演算法
#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;
} 
4.分組揹包
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]);