poj 3046 Ant Counting——多重集合的背包

题目:http://poj.org/problem?id=3046

多重集合的背包问题。

1.式子:考虑dp[ i ][ j ]能从dp[ i-1 ][ k ](max(0 , j - c[ i ] ) <= k <= j)转移来。

  对于j<=c[ i ],这就是前缀和一样,所以dp[ i ][ j ] = dp[ i ][ j-1 ] + dp[ i-1 ][ j ];

  对于j>c[ i ],加了dp[ i ][ j-1 ] + dp[ i-1 ][ j ]之后会多加了一项dp[ i-1 ][ j-c[ i ]-1 ],减掉即可。

2.意义:dp[ i-1 ][ j ]表示从前面组中选 j 个,dp[ i ][ j-1 ]表示从本组+前面组中选了 j-1 个,再在本组中选1个。

  有一个不合法的情况是dp[ i ][ j-1 ]中已经选了c[ i ]个本组的,就不能再在本组中选1个了。

  而 dp[ i ][ j-1 ]中已经选了c[ i ]个本组的 的方案数就是前面组中选了 j-1-c[ i ] 个的方案数(这样选 j-1 的时候就必须选c[ i ]个本组的了)。减掉即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005,M=1e5+5,mod=1e6;
int n,m,c[N],l,r,dp[2][M],ans;
int main()
{
    scanf("%d%d%d%d",&n,&m,&l,&r);int x;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);c[x]++;
    }
    dp[0][0]=dp[1][0]=1;
    for(int i=1;i<=n;i++)
    {
        int u=(i&1),v=!u;
        for(int j=1;j<=r;j++)
        {
            dp[u][j]=(dp[v][j]+dp[u][j-1])%mod;
            if(j>c[i])dp[u][j]=((dp[u][j]-dp[v][j-c[i]-1])%mod+mod)%mod;
        }
    }
    int u=(n&1);
    for(int j=l;j<=r;j++)(ans+=dp[u][j])%=mod;
    printf("%d",ans);
    return 0;
}

 

posted on 2018-06-29 10:45  Narh  阅读(126)  评论(0编辑  收藏  举报

导航