bzoj1026-windy数-数位DP-递推写法与递归写法

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

题意:传送门

 原题目描述在最下面。
 windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数?

思路:

递推:

dp[i][j]表示前 i 位第 i 个位置放上数字 j 的合法数字个数(从低位往高位的第 i 位)
dp[i][j] += dp[i-1][k] if(abs(j - k) >= 2)

对于数字num取出它的每一位后,考虑三种情况:(x是最高位数字)

一:首位填0:
ans += dp[i][j] i∈[1, pos-1], j∈[1, 9]

二:首位填[1, x - 1]:
ans += dp[pos][i] i∈[1, x - 1]

三:首位填x:

if(abs(j - ar[i+1])>=2) ans += dp[i][j] i∈[1, pos - 1], j ∈[0, 9]

记得如果这个数字本身也符合wind数,记得加1.

递归:

 拿普通数位DP的模板就行了。

 记录一下前导0和前一位数字dfs即可。

AC代码:

递推:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int N = 1e3+7;
const int INF = 0x3f3f3f3f;
LL n, m, ar[25];
LL dp[N][N];
LL ab(LL x){
  return x<0?-x:x;
}
void init(){
  for(int i = 0; i <= 9; ++i){
    dp[1][i] = 1;
  }
  for(int i = 2; i <= 10; ++i){
    for(int j = 0; j <= 9; ++j){
      for(int k = 0; k <= 9 ;++k){
        if(ab(j - k)>=2){
          dp[i][j] += dp[i-1][k];
        }
      }
    }
  }
}
LL slove(LL x){
  if(x <= 9)return x;
  int pos = 0;
  while(x){
    ar[++pos] = x%10;
    x /= 10;
  }
  LL sum = 0;
  //最高位取0
  for(int j = pos - 1; j >= 1; --j){
    for(int i = 1; i <= 9; ++i){
      sum += dp[j][i];
    }
  }
  //最高位取1-ar[pos]-1
  for(int i = 1; i < ar[pos]; ++i){
    sum += dp[pos][i];
  }
  //最高位取ar[pos]
  for(int i = pos-1; i >= 1; --i){
    for(int j = 0; j < ar[i]; ++j){
      if(ab(j-ar[i+1])>=2){
        sum += dp[i][j];
      }
    }
    if(ab(ar[i+1]-ar[i])<2)break;
    if(i == 1)sum++;
  }
  return sum;
}
int main(int argc, char const *argv[]){
  init();
  while(~scanf("%lld%lld", &n, &m)){
    printf("%lld\n", slove(m) - slove(n-1));
  }
  return 0;
}

递归:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int N = 1e5 + 7;
const int INF = 0x3f3f3f3f;
int n, m;
int ar[N], dp[20][10];
inline int ab(int x){
  return x<0?-x:x;
}
int dfs(int pos, int pre, bool lead, bool limit){
  if(pos == -1) return 1;
  if(!limit&&!lead&&dp[pos][pre] != -1) return dp[pos][pre];
  int up = limit? ar[pos]: 9, sum = 0;
  for(int i = 0; i <= up; ++i){
    if(pre==-1&&i==0){
      sum += dfs(pos-1,pre,lead,limit&&i==ar[pos]);
    }else if(ab(pre-i)>=2){
      sum += dfs(pos-1,i,lead&&i==0,limit&&i==ar[pos]);
    }
  }
  if(!limit&&!lead)dp[pos][pre] = sum;
  return sum;
}
int solve(int x){
  int pos = 0;
  while(x){
    ar[pos++] = x%10;
    x /= 10;
  }
  return dfs(pos-1, -1, 1, 1);
}
int main(){
  memset(dp, -1, sizeof(dp));
  while(~scanf("%d%d", &n, &m)){
    printf("%d\n", solve(m) - solve(n - 1));
  }
  return 0;
}


原题目描述:

bzoj1026-windy数-数位DP-递推写法与递归写法_第1张图片

你可能感兴趣的:(数位DP,基础DP/背包DP)