【JZOJ5184】Gift【DP】【01揹包】
阿新 • • 發佈:2018-11-10
題目大意:
題目連線:https://jzoj.net/senior/#main/show/5184
題目圖片:
http://wx4.sinaimg.cn/mw690/0060lm7Tly1fwmsvyi1y8j30ow0na0um.jpg
http://wx1.sinaimg.cn/mw690/0060lm7Tly1fwmswti8zjj30p20fnt9r.jpg
給出一串數字,要求選擇的數字和不能超過
,而且還能選就得選。求方案數。
思路:
01揹包變形。
設
表示選擇了第
到第
個數字,和為
的方案數。那麼很明顯就有
那麼,對於求答案,我們可以列舉現在沒有選擇的最小的數字
是
,那麼這也就說明
前面的數字(經過排序後就比它更小)都被選擇了。那麼再列舉一個
表示還能選擇比
小的數(按照原題來說就是還剩
塊錢),那麼
必須比
大,不然的話就可以再選擇
了。
那麼就設
表示前
個數的和。
那麼就有
時間複雜度:
程式碼:
#include <cstdio>
#include <algorithm>
#define N 1100
#define MOD 10000007
using namespace std;
int n,m,p[N],f[N][N],s[N],ans;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&p[i]);
sort(p+1,p+1+n);
for (int i=1;i<=n;i++)
s[i]=s[i-1]+p[i]; //求字首和
if (m>=s[n]) return !printf("1"); //特判,全部都可以選就只有一種情況
f[n+1][0]=1;
for (int i=n;i>=1;i--)
for (int j=0;j<=m;j++)
if (j>=p[i]) f[i][j]=(f[i+1][j]+f[i+1][j-p[i]])%MOD;
else f[i][j]=f[i+1][j];
for (int i=1;i<=n;i++)
for(int j=0;j<p[i];j++)
if (m-s[i-1]-j>=0)
ans=(ans+f[i+1][m-s[i-1]-j])%MOD;
printf("%d\n",ans);
return 0;
}