POJ-3093___Margaritas on the River Walk——01揹包的變異
阿新 • • 發佈:2018-12-12
題目連結:傳送門
題目大意:
多組樣例,在這裡我們假設有個物品,容量為的揹包,問有多少種方案,使得剩下的所有物品都裝不進揹包。。。。。。
解題思路:
假如在剩下的物品中,體積最小為的物品裝不進揹包,那麼很明顯所有揹包中體積小於的都被放進去了,依此思路,我們給所有揹包排個序,然後依次列舉每個揹包,將這個揹包當做剩下的體積最小的揹包,所以,當列舉到第個揹包時,第件揹包都被放進去了。 設第個揹包的體積為,前個物品的體積和為
當列舉到時,由於第件物品已被放入揹包,所以揹包剩餘容量為,這時,要使得第件物品放不進去,就要用第的物品裝滿剩餘容積為 ~ 的揹包,這樣使得第件物品恰好放不進揹包(揹包剩餘容量小於)。
加下來考慮列舉問題。
從順序列舉到,則第一次對做揹包,第二次對做揹包時間複雜度為 從逆序列舉到,則我們需要對做揹包,對做揹包,對做揹包,會發現,每次做揹包都是放入了個物品,顯然我們可以對此進行優化,在上一次做完揹包之後,把第件物品放入上一次做完的揹包,就得到了我們需要的狀態,這樣從頭到尾只做了一次揹包。
程式碼思路:
用存前件物品的和,每次列舉物品的時候,要注意小於的情況,這時用 注意,每次列舉的過程中,我們要先將第的物品裝滿剩餘容積為 ~ 的結果加入到 中,然後再將當前物品加入到揹包中
核心:靈活運用揹包順序與逆序的巧妙思路,最重要的是明白揹包dp的本質,本題就運用這個思路逆序更新揹包,實在巧妙
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, ans;
int dp[1005], w[35], sum[35];
int main() {
int cas, n, m, t=0;
scanf("%d", &cas);
while(cas--) {
sum[0]=ans=0;
memset(dp, 0, sizeof(dp));
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
scanf("%d", &w[i]);
sort(w+1, w+n+1);
for(int i=1; i<=n; i++)
sum[i]=sum[i-1]+w[i];
dp[0]=1;
for(int i=n; i>=1; i--) {
int k = max(m-sum[i-1]-w[i]+1, 0);
for(int j=m-sum[i-1]; j>=k; j--)
ans += dp[j];
for(int j=m; j>=w[i]; j--) //將第i個物品放入揹包,即重新編排了一次揹包
dp[j] += dp[j-w[i]];
}
if(m<w[1]) ans=0;
printf("%d %d\n", ++t, ans);
}
return 0;
}