MDeath-Kid

- M I T & Y
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

按位 DP

Posted on 2011-08-08 19:07  MDeath-Kid  阅读(742)  评论(0)    收藏  举报

第一次按位DP,按位DP主要是求在一区间上满足某种或几种关系的数字有多少个。

记得我从4月份的浙大月赛碰到第一个这样的题后,碰到了好多次,可都不会啊,现在终于会了这个DP。

HDU 3555这个多校的赛大概是很多人第一次的按位DP,我也是,理解按位DP用了一天+晚上的时间,虽然比赛的时候还A出来两个比较水的按位DP,但自己的思路狠不清晰,狠让我震惊的就算这个题的discuss,那位童鞋太萌了!!~

我为了理解按位DP到底是怎么运行的,我自己手动算了两张纸,而且是两边啊啊啊啊啊,因为不懂啊,知道那个状态设计,但是不知的实现起来怎么就那么的费劲啊啊啊,最后终于理解了,觉得用HDU那位童鞋的代码做模板了,十分感谢,其实比赛的时候就迷迷糊糊的A了。

两个比较说的按位DP,大家可以用这个入门。

HOJ 3038求区间内非递减的数字的个数。

/************
* int dp[i][j][k];
*
* [ i, j, k,] indicats
*
* 满足 : 在第 i 位,第 i 位的数字是 j ,
* 处于 k (越界状态) 状态时
* 的个数
*
* MAXN 数字上界的最长长度
************
*
* k 是否处在边界上
**********
*/
HOJ 3038
char a[MAXN],b[MAXN];
int dp[MAXN][10][2];

int DP_bit(const char * a) {
int alen = strlen(a);

int i,j,s,flag,ans=0;
for(i = 0;i<a[0] -'0';i++) dp[0][i][0] = 1;
dp[
0][i][1] = 1;
for(i = 1;i<alen ;i++) {
for(j = 0;j< a[i] - '0';j++) {
for(s = 0;s<= 9;s++) {
//唯一变化的就是这里了。
if(j >= s) {
dp[i][j][
0] += dp[i-1][s][0] + dp[i-1][s][1];
}
}
}
for(s = 0;s<= 9;s++) {
if(j >= s) {
dp[i][j][
0] += dp[i-1][s][0];
dp[i][j][
1] += dp[i-1][s][1];
}
}
for(j = a[i] - '0' +1;j<=9;j++) {
for(s = 0;s <= 9;s++) {
if(j >= s) {
dp[i][j][
0] += dp[i-1][s][0];
}
}
}
}
for (i=0;i<=9;i++) ans+=(dp[alen-1][i][0]+dp[alen-1][i][1]);
return ans;
}
int isa(char *a) {
int len = strlen(a);
for(int i = 1;i<len;i++) {
if(a[i-1] > a[i]) {
return 0;
}
}
return 1;
}
void init() {
scanf(
" %s",b);
SET(dp,
0);
int s = DP_bit(a);
SET(dp,
0);
int ss = DP_bit(b);//DB(s<<"--"<<ss);
printf("%d\n",ss-s+isa(a));
}

  HOJ 3039

在区间上各位数字的和是一个数的倍数的个数。

HOJ 3039
char a[MAXN],b[MAXN],m;
int dp[MAXN][MAXNT][2];

int DP_bit(const char * a) {
int alen = strlen(a);

int i,j,s,flag,ans=0;
for(i = 0;i<a[0] -'0';i++) dp[0][i][0] = 1;
dp[
0][i][1] = 1;
for(i = 1;i<alen ;i++) {
for(j = 0;j< a[i] - '0';j++) { //now
for(s = 0;s<= 9*(i+1);s++) { //pre
dp[i][j+s][0] += dp[i-1][s][0] + dp[i-1][s][1];
}
}
for(s = 0;s<= 9*(i+1);s++) {
dp[i][j
+s][0] += dp[i-1][s][0];
dp[i][j
+s][1] += dp[i-1][s][1];
}
for(j = a[i] - '0' +1;j<=9;j++) {
for(s = 0;s<= 9*(i+1);s++) {
dp[i][j
+s][0] += dp[i-1][s][0];
}
}
}
for (s=0;s<= 9*(i+1);s++) ans+= s%m == 0?(dp[alen-1][s][0]+dp[alen-1][s][1]):0;
return ans;
}
int isa(char *a) {
int len = strlen(a),sum=0;
for(int i = 0;i<len;i++) {
sum
+= a[i] - '0';
}
if(sum % m == 0)
return 1;
else return 0;
}
void init() {
scanf(
" %s",b);
scanf(
"%d",&m);
SET(dp,
0);
int s = DP_bit(a) - 1;
SET(dp,
0);
int ss = DP_bit(b) - 1;//DB(s<<"--"<<ss);
printf("%d\n",ss-s+isa(a));
}

  仔细看着两个代码,发现改变的就是最里面的东西了,贴下自己的模板

View Code
 1 char a[MAXN],b[MAXN];
2 int dp[MAXN][10][2];
3
4 int DP_bit(const char * a) {
5 int alen = strlen(a);
6
7 int i,j,s,flag,ans=0;
8 for(i = 0;i<a[0] -'0';i++) dp[0][i][0] = 1;
9 dp[0][i][1] = 1;
10 for(i = 1;i<alen ;i++) {
11 for(j = 0;j< a[i] - '0';j++) {
12 for(s = 0;s<= 9;s++) {
13 //code here
14 }
15 }
16 for(s = 0;s<= 9;s++) {
17 //code here
18 }
19 for(j = a[i] - '0' +1;j<=9;j++) {
20 for(s = 0;s <= 9;s++) {
21 //code here
22 }
23 }
24 }
25 for (i=0;i<=9;i++) ans+=(dp[alen-1][i][0]+dp[alen-1][i][1]);
26 return ans;
27 }

  code here写需要转移的,自己设计的状态就行了!