1. 程式人生 > >dp復習 背包[禮物]

dp復習 背包[禮物]

一個數 排序 ++i return 我們 麻煩 blog 取模 cnblogs

【問題描述】
人生贏家老王在網上認識了一個妹紙,然後妹紙的生日到了,為了表示自己的心
意,他決定送她禮物。可是她喜愛的東西特別多,然而他的錢數有限,因此他想
知道當他花一定錢數後剩余錢數無法再購買任何一件剩余物品(每種物品他最多
買一個)時有多少種方案,兩種方案不同,當且僅當兩種方案中至少有一件品不
同,可是由於他忙著準備泡下一個妹紙(chi),因此麻煩聰明的你幫幫忙。
【輸入格式】
輸入第一行 n 和 m, n 表示妹紙喜歡的禮物數目, m 表示現有的錢數,第二行 n
個數,表示 n 個物品的價格。
【輸出格式】
輸出一行一個數表示方案數目,答案對 1000000007 取模。
【 樣例輸入 1】
gift.in
6 25
8 9 8 7 16 5
【樣例輸出 1】
gift.out
15
【數據範圍】
30%的數據: 0<=n<=100 0<=m<=500
100%的數據:0<=n<=1000 0<=m<=1000
註意:所有物品價格均小於 m

題解:(wfj提供的思路)

我們想如果我們設dp[i][j]表示前i個物品容量為j的方案數是多少,這個很好轉,做背包就可以了,但怎麽統計答案呢,我們可以先把物品從大到小排序,然後每次強制讓第i個物品做我們沒選的最小的數,那麽後面比他小的就都要選上,前面就做一個背包就可以了,然後限制j自己推一下就看以了,最有不要忘記,如果可以全部選上也是一種方案。

代碼:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<cstring>
#define
ll long long const int MAXN=1010; const int mod=1000000007; using namespace std; ll dp[MAXN][MAXN],n,m,minn,w[MAXN]; ll sum[MAXN]; int cmp(int a,int b){ return a>b; } int main(){ scanf("%lld%lld",&n,&m); for ( int i=1;i<=n;++i) scanf("%lld",&w[i]); dp[0][0]=1; sort(w+1,w+n+1,cmp);
for (int i=n;i;--i) sum[i]=sum[i+1]+w[i]; ll ans=0; for(int i=1;i<=n;i++){ for(int j=m-sum[i+1];j>=0&&j>m-sum[i+1]-w[i];j--) ans+=dp[i-1][j],ans%=mod; for(int j=m;j>=0;j--){ dp[i][j]=dp[i-1][j]; if(j-w[i]>=0) dp[i][j]+=dp[i-1][j-w[i]],dp[i][j]%=mod; } } if(sum[1]<=m) ans++,ans%=mod; printf("%lld\n",ans); }

dp復習 背包[禮物]