版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/83446767
题目大意:
题目连接: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;
}