首页 > 代码库 > HDU 4906 Our happy ending

HDU 4906 Our happy ending

题意:

  Given a sequence a_1,a_2,...,a_n, if we can take some of them(each a_i can only be used once), and they sum to k, then we say this sequence is a good sequence.
   How many good sequence are there? Given that each a_i is an integer and 0<= a_i <= L.

  给你一个序列: a_1, a_2, ..., a_n,如果我们能够取出他们中的一些(每个a_i只能取一次),并且他们的和是k,我们就把这个序列称为一个好的序列。

  如果每个a_i都是0到L中的整数,那么他们一共能组成多少好序列呢?

 

思路:

  状态压缩。

  dp[i][S]表示长度为i的序列,能组合成的加和的集合为S的情况有多少种(集合用数字的位来表示,1表示可以组成,0表示不可以。因为有用的数字最多只有20,所以可以这样表示)。

  这样,我们可以枚举第i+1位,得到一个新的可以组成的数的集合。原理和背包类似。 在和别人的讨论中发现,用位运算可以很方便的表示这个背包的内容,我们假设原本可以组成的集合为S,现在枚举放不放进背包的数是j。那么,不放进背包的时候可能组成的集合还是S;而放进背包的话,可能组成的集合就变成了(S<<j);所以枚举j可能组成的所有集合就是 S|(S<<j) 看起来是不是很简洁。

  所以这样,我们的转移方程就可以写成 dp[i+1][S] = sum{dp[i][S0] | (S0|(S0<<j) ) == S}

  值得注意的是,直接开 n*2^n 的数组可能会爆内存,观察转移是一层一层的进行的,所以我们可以用滚动数组进行优化。

(最后,感谢alpc同学的耐心讲解 :)

 

代码:(这样做会跑2秒,真心不知道那些0毫秒的怎么做的Orz)

 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <algorithm> 7 #include <string> 8 #include <queue> 9 #include <stack>10 #include <vector>11 #include <map>12 #include <set>13 #include <functional>14 #include <time.h>15 16 using namespace std;17 18 typedef __int64 ll;19 20 const int INF = 1<<30;21 const int MAXN = 21;22 const ll MOD = (ll) 1e9+7;23 24 ll dp[1<<MAXN];25 int n, k, L;26 27 void solve() {28     memset(dp, 0, sizeof(dp));29     int MIN = min(L, k);30     int FULL = (1<<(k+1))-1; //全集31 32     dp[1] = 1;33     for (int i = 0; i < n; i++) {34         for (int S = FULL; S > 0; S--) if (dp[S]>0) {35             ll tmp = dp[S];36             for (int j = 1; j <= MIN; j++)  //枚举每一个有效数字37                 dp[FULL&(S|(S<<j))] = (dp[FULL&(S|(S<<j))]+tmp)%MOD;38             if (MIN<L)39                 dp[S] = (dp[S]+((L-MIN)*tmp)%MOD)%MOD;40         }41     }42 43     ll ans = 0;44     for (int S = 1; S <= FULL; S++) if (S&(1<<(k)))45         ans = (ans+dp[S])%MOD;46 47     printf("%I64d\n", ans);48 }49 50 int main() {51     #ifdef Phantom0152         freopen("HDU4906.txt", "r", stdin);53     #endif //Phantom0154 55     int T;56     scanf("%d", &T);57     while (T--) {58         scanf("%d%d%d", &n, &k, &L);59         solve();60     }61 62     return 0;63 }
View Code