HDOJ 4906 Our happy ending 状压DP(数位DP?)

http://acm.hdu.edu.cn/showproblem.php?pid=4906

题意:

N个数的序列,每个数可以选择填0-L,如果一个序列可以选出某些数,他们的和为K,那么这个序列就是”好序列“,给定N<=20,K<=20,0<=L<=10^9,问好序列的个数。

分析:

N和K很小,所以要想办法利用这个特性(状压?搜索?)。虽然L很大,但实际上一个数大于K的时候,肯定是不能选他组成K的。我们就先考虑L<=K的做法。

然后还是考虑不出来。。

好吧,看题解吧。。

目前能搜到的题解都写得比较简单,我稍微详细一些?

dp[i][j],j用二进制来表示,第k位为1,说明已有的序列能选出几个数使得和为k,dp[i][j]就表示前i个数的序列能表示j的方案数。

初始dp[0][0]=1,answer是 Σdp[n][j],其中j的第K位为1。

转移的话,逆推有点难想,还是顺推吧,第i+1个数,从0到L,我们分成0,1--K,K+1--L来考虑:

如果第i+1位选择0,那么dp[i][j]可以转移给dp[i+1][j],因为多了个0,能表示的和(j)还是一样。

如果第i+1位选择K+1到L中的数,那么dp[i][j]可以转移给dp[i+1][j],原因同上。

如果第i+1位选择1到K中的数,假设是x,那么dp[i][j]可以转移给dp[i+1][to],to = j|1<<(x-1)|(j<<x)&((1<<K)-1)

(重点)to中的j表示,我们不选择当前这个x能表示的和,1<<(x-1)表示只选择当前这个x能表示的和,最后一项是在之前已经能表示的和上再加上一个x,这些新的和我们都能表示了,同时为了不让他超过最大值,所以再和最大值按位与。

然后像背包一样倒序枚举,就可以省略掉i这一维。

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 const int mod = (int)1e9+7;
 7 int T, N, K, L, MAX, extra;
 8 long long dp[(1<<20)+10];
 9 int main()
10 {
11     scanf("%d", &T);
12     while(T--)
13     {
14         scanf("%d%d%d", &N, &K, &L);
15         memset(dp, 0, sizeof(dp));
16         if (L > K){
17             extra = L - K;
18             L = K;
19         }
20         else extra = 0;
21         dp[0] = 1;
22         MAX = (1 << K) - 1;
23         for (int i = 1; i <= N; i++)
24             for (int j = MAX; j >= 0; j--){
25                 if (dp[j] == 0) continue;
26                 long long tmp = dp[j];
27                 for (int k = 1; k <= L; k++){
28                     int to = j | 1<<(k-1) | ((j<<k)&MAX);
29                     dp[to] += tmp;
30                     if (dp[to] >= mod) dp[to] -= mod;
31                 }
32                 //if (dp[j] != tmp) printf("%d %lld %lld\n", j, dp[j], tmp);
33                 dp[j] = (dp[j] + tmp * extra) % mod;
34             }
35         long long ans = 0;
36         for (int i = 1 << (K-1); i <= MAX; i++){
37             ans += dp[i];
38             if (ans >= mod) ans -= mod;
39         }
40         printf("%I64d\n", ans);
41     }
42     return 0;
43 }

HDOJ 4906 Our happy ending 状压DP(数位DP?)

时间: 2024-08-03 00:35:06

HDOJ 4906 Our happy ending 状压DP(数位DP?)的相关文章

HDU 4906 Our happy ending (状压DP)

HDU 4906 Our happy ending 题目链接 题意:给定n个数字,每个数字可以是0-l,要选其中一些数字,然后使得和为k,问方案 思路:状压dp,滚动数组,状态表示第i个数字,能组成的数字状态为s的状态,然后每次一个数字,循环枚举它要选取1 - min(l,k)的多少,然后进行状态转移 代码: #include <cstdio> #include <cstring> typedef long long ll; const int N = (1<<20)

hdu4906 Our happy ending --- 状压dp

给一个n个数的数列,从中取一些数构成新数列, 如果新数列中有一些数的和是k,那么这就是一个好数列,问这样的数列的个数. 从1-n位枚举其取值从1-min(l,k),来更新可达状态. dp[i]中i的二进制每一位表示和(1-k),1表示可以取到,0表示取不到. #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #in

UVA - 10817 Headmaster&#39;s Headache (状压类背包dp+三进制编码)

题目链接 题目大意:有S门课程,N名在职教师和M名求职者,每名在职教师或求职者都有自己能教的课程集合以及工资,要求花费尽量少的钱选择一些人,使得每门课程都有至少两人教.在职教师必须选. 可以把“每个课程已经分别有几个人教”作为状态来进行转移,每个人能教的课程集合作为“物品重量”,工资作为“价值”来更新dp值,类似01背包,每放进一个人,从后往前更新即可. 状态的表示可以用三进制编码,为了写起来舒服,我写了个结构体作为状态和编码转换的桥梁,也可以进行状态的“加法运算”,虽然速度比较慢就是了~~ 有

HDOJ 5418 Victor and World 状压DP

水状压DP Victor and World Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/131072 K (Java/Others) Total Submission(s): 407    Accepted Submission(s): 164 Problem Description After trying hard for many years, Victor has finally received a p

our happy ending(状压dp)

题意:给定一个n,k,l. 问有多少长度为n的序列满足选出一些数使得他们相加为k,数列中每个数都在1-l以内. Solution 正解还是很妙的. 状压dp,设dp[i][j]表示长度为i的序列,能表示出集合为j的序列个数. 这个状态非常好,我们每局下一个可填的数,可选集合就变成了j|(1<<p-1)|(j<<p&size) Code #include<iostream> #include<cstdio> #include<cstring>

HDU 4336-Card Collector(状压,概率dp)

题意: 有n种卡片,每包面里面,可能有一张卡片或没有,已知每种卡片在面里出现的概率,求获得n种卡片,需要吃面的包数的期望 分析: n很小,用状压,以前做状压时做过这道题,但概率怎么推的不清楚,现在看来就是基本的概率dp dp[s]表示获得卡片种数情况是s时期望包数,dp[(1<<n)-1]=0,dp[0]就是答案 dp[s]=sum(dp[s+(1<<j)]*p[j])+1+(1-tmp)*dp[s](tmp是未吃到的卡片的概率和) 移项化简即可 #include <map&

HDU 4336 Card Collector(容斥原理 or 状压求期望dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336 Problem Description In your childhood, do you crazy for collecting the beautiful cards in the snacks? They said that, for example, if you collect all the 108 people in the famous novel Water Margin,

数位dp模板 [dp][数位dp]

现在才想到要学数位dp,我是不是很弱 答案是肯定的 以一道自己瞎掰的题为模板 1 //题: 2 //输入数字n 3 //从0枚举到n,计算这n+1个数中含有两位数a的数的个数 4 //如12930含有两位数93 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 using namespace std; 9 10 int n,m=0,a,g1,g2; 11 int dp[10][10][2],lim

UVA 11600-Masud Rana(状压,概率dp)

题意: 有n个节点的图,开始有一些边存在,现在每天任意选择两点连一条边(可能已经连过),求使整个图联通的期望天数. 分析: 由于开始图可以看做几个连通分量,想到了以前做的一个题,一个点代表一个集合(这里是连通分量)进行压缩 dp[i][s]表示最后连接的第i个联通分量,联通状态是s时的期望天数,dp[0][1],即为答案,由于s可能很大,用记忆化搜索 #include <map> #include <set> #include <list> #include <c