1. 程式人生 > >2018年10月27提高組 T1 Gift

2018年10月27提高組 T1 Gift

大意

給定nn個物品,每個物品只能選一次,求出在空間不超過mm且不能再選任何一個物品時的方案數對107+710^7+7取模

思路

發現這玩意兒很像01揹包啊,於是就有了

f[i][j]=f[i1][j]+f[i1][ja[i]]f[i][j]=f[i-1][j]+f[i-1][j-a[i]]

然後呢,因為有不能再選任何一件物品這個約束,所以我們先排序,保證前面的小,然後列舉不能再選的物品,接著轉移,這樣子的複雜度是O(n2m)O(n^2m)的,期望得分60

接著我們發現,上述演算法因為每次都dpdp一次所以時間不夠,於是我們就想到了能否提前求好所有的值呢?答案是可以的。

首先每次我dpdp時只是不能選的物品多了一個,而其他地方實際上市沒有區別的,所以我們換一種表示方法

f[i][j]f[i][j]表示後面的ii個數,還剩jj點空間時的方案數,得到方程:

f[i][j]=f[i+1][j]+f[i+1][ja[i]]f[i][j]=f[i+1][j]+f[i+1][j-a[i]]

這樣我們每次轉移實際上就是ii往前挪一位,就不需要再用dpdp區轉移了,時間複雜度O(nm)O(nm)

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm> #define ymw 10000007 using namespace std; int a[1001],n,m,minn=2147,sum; long long ans,f[1005][1001],now; inline void write(long long x){if(x>9)write(x/10);putchar(x%10+48);return;} signed main() { freopen("1.txt","r",stdin); scanf("%d%d",&n,&m); for(register int i=1;i<=
n;i++) scanf("%d",a+i),sum+=a[i]; if(sum<=m) return putchar(49)&0;//加在一起都沒有m,那麼只能全部都選 sort(a+1,a+1+n); f[n+1][0]=1; for(register int i=n;i>0;i--) for(register int j=0;j<=m;j++) { f[i][j]=f[i+1][j]; if(j>=a[i])(f[i][j]+=f[i+1][j-a[i]])%=ymw;//動態轉移 } for(register int x=1;x<=n;x++) { for(register int i=0;i<a[x];i++) if(m-i>=0) (ans+=f[x+1][m-i])%=ymw;//計算在後面x+1個數的方案數 m-=a[x];//選走這個物品,容量變少了 } write(ans%ymw);//輸出 }