礼物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
*/

 

posted @ 2018-09-07 22:03  CaptainLi  阅读(213)  评论(0编辑  收藏  举报