码迷,mamicode.com
首页 > Windows程序 > 详细

[SCOI2009] Windy数 - 数位dp

时间:2020-02-04 15:42:14      阅读:110      评论:0      收藏:0      [点我收藏+]

标签:个数   选择   正整数   ret   line   turn   cpp   bre   印象   

调数位,两行泪

好久没写数位dp了,这当然是因为队友zyf大佬dp实在太猛,orzorz

印象中唯一写过一次是在某一次区域赛的热身赛上(而且我还写翻车了)

所以今天的主题就是数位DP吧

不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。在A和B之间,包括A和B,总共有多少个windy数?

\(f[i][j]\) 从低到高 \(i\) 位,第\(i\)位是\(j\)的方案数

初态:\(f[1][i] = 1\)

转移:\(f[i][j]=f[i-1][k]\), 需要满足若干约束条件

如何统计答案?

对于位数比 \(n\) 小的,直接统计即可

对于位数相同的,枚举哪一位开始比 \(n\) 小,这样更高的位就确定了,更低的位只需要暴力选择即可

#include <bits/stdc++.h>
using namespace std;

int f[15][15];
const int m = 10;
int t[15];

int calc(int n) {
    if(n==0) return 0;
    int ans = 0;
    int x = n, y = 0;
    while(x) {
        t[++y] = x % 10;
        x /= 10;
    }
    int len = y;
    for(int i=1;i<len;i++) {
        for(int j=1;j<=9;j++)
            ans += f[i][j];
    }
    for(int i=len;i>=0;--i) {
        if(i==0) {++ans; break;}
        if(i<len-1 && abs(t[i+1]-t[i+2])<2) break;
        for(int j=(i==len?1:0);j<=9;j++) {
            if(j < t[i] && (i==len || abs(t[i+1]-j)>=2)) {
                if(i==1) ++ans;
                else for(int k=0;k<=9;k++) {
                    if(abs(k-j)>=2) {
                        ans += f[i-1][k];
                    }
                }
            }
        }
    }
    return ans;
}

int main() {
    for(int i=0;i<=9;i++) f[1][i]=1;
    for(int i=2;i<=m;i++) {
        for(int j=0;j<=9;j++) {
            for(int k=0;k<=9;k++) {
                if(abs(j-k)>=2) {
                    f[i][j] += f[i-1][k];
                }
            }
        }
    }
    int a,b;
    cin>>a>>b;
    cout<<calc(b) - calc(a-1)<<endl;
}

[SCOI2009] Windy数 - 数位dp

标签:个数   选择   正整数   ret   line   turn   cpp   bre   印象   

原文地址:https://www.cnblogs.com/mollnn/p/12259558.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!