HDU 5800 To My Girlfriend

题目:To My Girlfriend

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5800

题意:

  给你n、s,接下来n个数,定义 f ( i , j , k , l , m )表示下标为i、j的必选,k、l的必不选,且和为m的 子集 数量。

  

  然后求上图式子的值。

思路:

  刚开始想着i 个数的和小于等于s 的子集数量,定义了dp[i][j]表示i 个数,和为j 的情况数量,不管怎么优化死活要n^3级别。看了题解,才觉醒。。。

  定义dp[i][j][ii][jj]表示:前i个数中,和达到j,ii个必选,jj个必不选的情况数量。ii、jj范围为0-2,四重循环就可以了。

  分两种:

  1. 第i 个数可选可不选,那么dp[i][j][ii][jj] += dp[i-1][j][ii][jj] + dp[i-1][j-a[i]][ii][jj]。

  2. 又分两种:

    1. 第i个数必选,dp[i][j][ii][jj] += dp[i-1][j-a[i]][ii-1][jj]。

    2. 第i个数必不选,dp[i][j][ii][jj] += dp[i-1][j][ii][jj-1]。

AC代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define Mod 1000000007
 4 typedef long long LL;
 5 int a[1010];
 6 int dp[1010][1010][3][3];
 7 void add(int &x,int y)
 8 {
 9   x=x+y;
10   if(x>=Mod) x-=Mod;
11 }
12 int main()
13 {
14   int t,n,m;
15   scanf("%d",&t);
16   while(t--)
17   {
18     scanf("%d%d",&n,&m);
19     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
20     memset(dp,0,sizeof(dp));
21     dp[0][0][0][0]=1;
22     for(int i=1;i<=n;i++)
23     {
24       for(int j=m;j>=0;j--)
25       {
26         for(int ii=2;ii>=0;ii--)
27         {
28           for(int jj=2;jj>=0;jj--)
29           {
30             add( dp[i][j][ii][jj] , dp[i-1][j][ii][jj] );
31             if(j+a[i]<=m) add( dp[i][j+a[i]][ii][jj] , dp[i-1][j][ii][jj] );
32             if(ii > 0 && j+a[i]<=m) add( dp[i][j+a[i]][ii][jj] , dp[i-1][j][ii-1][jj] );
33             if(jj > 0) add( dp[i][j][ii][jj] , dp[i-1][j][ii][jj-1] );
34           }
35         }
36       }
37     }
38     LL ret=0;
39     for(int i=2;i<=m;i++)
40     {
41       ret=ret+dp[n][i][2][2];
42     }
43     printf("%I64d
",ret*2*2%Mod);
44   }
45   return 0;
46 }
原文地址:https://www.cnblogs.com/hchlqlz-oj-mrj/p/5738480.html