HDU 5800 (DP)

Problem To My Girlfriend (HDU 5800)

题目大意

  给定一个由n个元素组成的序列,和s (n<=1000,s<=1000)

  求 : 

   

  f (i,j,k,l,m) 指必定选第i,j号元素,必定不选k,l号元素,选的元素总和为m的子集个数。

解题分析

  一开始想了个n^3的DP,f[j][k]表示选j个数总和为k的方案数,然后一直想着怎么去优化,陷进死胡同,到比赛结束还没想出来。

  看了题解后,感觉智商被藐视了。

  题解的做法是f[i][j][s1][s2]表示前i个数总和为j必选s1个必不选s2个的方案数,这样是O(n*s*9)的。

  对于每一个数,有4种选法:选,不选,必选,必不选,然后转移就好了。

  答案就是sigma(f[n][i][2][2]) ,i∈[0,s]。

参考程序

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6 
 7 #define N 1008
 8 #define mo 1000000007
 9 
10 int dp[N][N][3][3];
11 int a[N];
12 
13 void add(int &x,int y){
14     x = x + y;
15     if (x >= mo) x -= mo;
16 }
17 int main(){
18     int T;
19     scanf("%d",&T);
20     while (T--){
21         int n,s;
22         scanf("%d%d",&n,&s);
23         for (int i=1;i<=n;i++) scanf("%d",&a[i]);
24         memset(dp,0,sizeof(dp));
25         dp[0][0][0][0]=1;
26         for (int i=1;i<=n;i++)
27             for (int j=0;j<=s;j++)
28                 for (int s1=0;s1<=2;s1++)
29                     for (int s2=0;s2<=2;s2++){
30                         add(dp[i][j][s1][s2],dp[i-1][j][s1][s2]);
31                         if (j>=a[i]) add(dp[i][j][s1][s2],dp[i-1][j-a[i]][s1][s2]);
32                         if (s1>0 && j>=a[i])
33                             add(dp[i][j][s1][s2],dp[i-1][j-a[i]][s1-1][s2]);
34                         if (s2>0)
35                             add(dp[i][j][s1][s2],dp[i-1][j][s1][s2-1]);
36                     }
37         int ans=0;
38         for (int i=0;i<=s;i++) add(ans,dp[n][i][2][2]);
39         printf("%I64d\n",ans*4ll % mo);
40     }
41 }
View Code

 

posted @ 2016-08-04 19:46  rpSebastian  阅读(591)  评论(0编辑  收藏  举报