hdu 5800 To My Girlfriend(背包变形)

To My Girlfriend

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 609    Accepted Submission(s): 236



Problem Description
Dear Guo

        I never forget the moment I met with you.You carefully asked me: "I have a very difficult problem. Can you teach me?".I replied with a smile, "of course"."I have n items, their weight was a[i]",you said,"Let's define f(i,j,k,l,m) to be the number of the subset of the weight of n items was m in total and has No.i and No.j items without No.k and No.l items.""And then," I asked.You said:"I want to know
i=1nj=1nk=1nl=1nm=1sf(i,j,k,l,m)(i,j,k,laredifferent)


Sincerely yours,
Liao
 

Input
The first line of input contains an integer T (T15) indicating the number of test cases.
Each case contains 2 integers n , s (4n1000,1s1000) . The next line contains n numbers: a1,a2,,an (1ai1000) .
 

Output
Each case print the only number — the number of her would modulo 109+7 (both Liao and Guo like the number).

 

Sample Input
  
  
2 4 4 1 2 3 4 4 4 1 2 3 4
 

Sample Output
  
  
8 8
 

Author
UESTC
 

Source
2016 Multi-University Training Contest 6


这道题题意有些难理解:
      给你n个数,定义f(i,j,k,l,m)为在n个数中,你选取的序列,序列满足必须包含第i,j个,不包含第l,m个元素,当然也可以包含其他的元素,这些序列的总为m,f则代表
有多少序列满足以上条件。
   题目给定 s,需要我们求出m属于[0,s]的序列总数。

多校赛的时候一直想的是求出固定m值,有多少不同个数的和为m,并统计个数相同时的数目,可这毫无疑问是要n^3的时间复杂度,所以华丽丽的超时了。。。

题解出来之后,发现这就是一个背包的变形题罢了,没做出来感觉好忧伤。

题目要求等于m的序列中一定包含2个,不包含2个
定义dp[i][j][s1][s2]  代表前i个和为j的一定包含s1个,不包含s2个的序列个数。

若以a[i]代表序列

dp[i][j][s1][s2] 有4个表达式
    dp[i-1][j-a[i]][s1-1][s2]     第 i 个算在一定被包含。
   dp[i-1][j][s1][s2-1]            第i 个算在一定不被包含。
  dp[i-1][j][s1][s2]                 第i 个不在序列里,但是不计算在s1,s2中。
   dp[i-1][j-a[i]][s1][s2]         第i 个在序列中,但是不计算在s1,s2中。

做的时候超内存了,所以改成滚动数组了。。。

代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1010;
int num[maxn];
typedef long long ll;
ll dp[2][maxn][3][3];
const int mod=1e9+7;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,s;
        scanf("%d%d",&n,&s);
        for(int i=1;i<=n;i++)
            scanf("%d",&num[i]);
        memset(dp,0,sizeof(dp));
        dp[0][0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
             int k=i&1;
             memset(dp[k],0,sizeof(dp[k]));
             for(int j=0;j<=s;j++)
             {
                for(int s1=0;s1<=2;s1++)
                for(int s2=0;s2<=2;s2++)
                {
                   dp[k][j][s1][s2]=dp[k^1][j][s1][s2];
                   if(j>=num[i])
                    dp[k][j][s1][s2]+=dp[k^1][j-num[i]][s1][s2];
                   if(s1>0&&j>=num[i])
                       dp[k][j][s1][s2]+=dp[k^1][j-num[i]][s1-1][s2];
                   if(s2>0)
                      dp[k][j][s1][s2]+=dp[k^1][j][s1][s2-1];
                   dp[k][j][s1][s2]%=mod;
                }
            }
        }
        ll ans=0;
        n&=1;
        for(int i=0;i<=s;i++)
        {
            ans+=dp[n][i][2][2];
            ans%=mod;
        }
        ans=ans*4;
        ans%=mod;
        printf("%I64d\n",ans);
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值