1. 程式人生 > >多重背包 /// DP+deque oj1943

多重背包 /// DP+deque oj1943

logs b- 滑動 target class view alt [] 出現

題目大意:

em.... 就是多重背包

挑戰340頁的東西 ...自己的筆記總結總是比較亂的

重點:原始的狀態轉移方程中 更新第 i 種物品時 重量%w[i] 的值不同 則它們之間是相互獨立的;

1--- 就是說在考慮第 i 種物品拿幾個時,w[i]+1 與 2*w[i]+1 與...與 k*w[i]+1 相互之間是有關聯的

  但 w[i]+j ... k*w[i]+j (j不為1) 與 w[i]+1 是相互獨立 無關的,即%w[i]的值不同時 相互獨立

2--- 那麽可以將 w[i]+j 與 2*w[i]+j 與...與 k*w[i]+j 的最優解都壓在 j 中,因為只要知道 j 的最優解

  並且知道 k(個數),就知道 k*w[i]+j的最優解 = j的最優解 + k*v[i]

用雙端隊列維護不同余數 j 的最優解,采用題目 滑動最小值 的方法維護 m[i]個物品在不同重量區間 的最大值

技術分享圖片
#include <bits/stdc++.h>
using namespace std;
int n,W,w[10005],v[10005],m[10005],dp[10005];
int deq[10005],deqv[10005]; // 模擬deque deq保存下標 deqv保存值
int main()
{
    int t;
    while(~scanf("%d",&t)) {
        
while(t--) { scanf("%d%d",&n,&W); for(int i=0;i<n;i++) scanf("%d%d%d",&w[i],&v[i],&m[i]); memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) // 第i種物品 for(int j=0;j<w[i];j++) { // 枚舉不同余數 int
head=0, tail=0; /// 雙端隊列維護余數為 j 時的最優解 // 則每個 j 開始時都應該賦0清空隊列 for(int k=0;k*w[i]+j<=W;k++) { // 枚舉該物品個數 int nowv=dp[k*w[i]+j]-k*v[i]; /// 原本重量為 k*w[i]+j 的最優解 // 可能其他種物品有更新過該重量的最優解 /// 壓到 j 時對應的最優解 while(head<tail && deqv[tail-1]<=nowv) tail--; /// 與曾中出現過存放在隊列中的最優解比較 deq[tail]=k, deqv[tail++]=nowv; // 更新隊列 隊頭head對應的即為最優解 dp[k*w[i]+j]=deqv[head]+k*v[i]; // 更新dp[]保存的最優解 if(deq[head]==k-m[i]) head++; /// 如果head對應的已經是m[i]個中的第一個 /// 即到下一個時 head對應的物品數量會超出m[i]的限制 /// 則應該舍棄這個最優解 把隊頭head移向下一位 } } printf("%d\n",dp[W]); } } return 0; }
View Code

多重背包 /// DP+deque oj1943