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 }