礼物gift(DP)
这道题的DP非常的有意思……
一开始我们总是会以为这是一个背包问题,直接dp[0] = 0,dp[j] += dp[j-c[i]]进行转移。之后统计一下从dp[m-minn]~dp[m]的答案之和为结果。
其中minn指花费最小的那个物品的花费。
不过这样是会丢解的。因为统计的过程我们只统计了不选最小的时候的情况,我们其实完全没有考虑到,选择了比一个物件花费小的所有物件却没有选择这个物件的情况。也就是说,上面的做法其实是相当于强制性不取最小的+全取的情况之和。
那么我们就应该选择强制性不取第2,3,……n个,然后保证前面比它们小的每一个都被选取,而更大的就直接背包dp,最后统计一下选不到当前这个物品共有多少种情况。也就是先从小到大排个序,之后依次处理就可以了。
啊啊啊怎么也想不到真是弱死了orz
#include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 1005; const int mod = 1e7+7; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int m,n,c[M],dp[M],minn = 2147483647,ans; int main() { // freopen("gift.in","r",stdin); // freopen("gift.out","w",stdout); n = read(),m = read(); rep(i,1,n) c[i] = read(); sort(c+1,c+1+n); rep(k,0,n) { m -= c[k]; if(m < 0) break; if(m == 0 || m < c[k+1] || k == n) { ans++; break; } memset(dp,0,sizeof(dp));dp[0] = 1; rep(i,k+2,n) per(j,m,c[i]) dp[j] += dp[j-c[i]],dp[j] %= mod; rep(j,m-c[k+1]+1,m) ans += dp[j],ans %= mod; } printf("%d\n",ans); return 0; } /* 6 25 8 9 8 7 16 5 30 250 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 */
当你意识到,每个上一秒都成为永恒。