【數位dp入門】51nod 1009 數字1的數量

CN_swords發表於2017-05-15

給定一個十進位制正整數N,寫下從1開始,到N的所有正數,計算出其中出現所有1的個數。

N(1 <= N <= 10^9)


dp[i] 表示 0 ~ (10 ^ i - 1) 中1的個數。

ten[i] 表示 10 ^ i 的值

pos 當前處理的位

num 當前處理的位前面已經有的1個數

limit 當前位有沒限制

其實dp可以看作記錄過程答案的dfs。(即是記憶化搜尋) 可以把這題當作dfs做。

對於 0 ~ 4321 有多少個1呢?

圖:

修正:雖然這題過掉了,後來做 hdu 2098 ,根據我自己的理解碼程式碼,無限wa,不知道是錯在哪裡,後來強行手跑程式碼,發現先前的理解不準確。

進一步理解:數位dp的記憶化搜尋不能理解為邊爆搜邊記錄,這樣是沒有意義的(之前腦子懵),如上圖:它先在最深一層搜10次(0000~0009),記錄dp值,運用在最後第二層搜10次(001X~009X)上,以此類推,搞出所有需要的dp值無非只搜了 (位數*10)次;


#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<map>
#include<queue>
#include<cmath>
#include<algorithm>
#include<deque>
typedef long long LL;
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
const int INF=0x3f3f3f3f;
const int N = 20;
const double eps = 1e-6;


int ten[N]; //10^i 的值
void init()
{
    ten[0] = 1;
    for(int i = 1; i < 10; i++)
        ten[i] = ten[i-1]*10;
}

char s[N];
int len;
int dp[N]; //0->10^i-1   中1的個數
int dfs(int pos,int num, int limit)
{
    if(pos == len)
        return num;
    if(!limit && dp[len-pos-1] != -1)
        return num*ten[len-pos]+dp[len-pos-1];
    int up = limit ? s[pos]-'0' : 9;
    int tmp = 0;
    for(int i = 0; i <= up; i++)
        tmp += dfs(pos+1,num+(i==1),limit && s[pos]-'0' == i);
    if(!limit)
        dp[len-pos-1] = tmp;
    return tmp;
}

int main()
{
    init();
    scanf("%s",s);
    len = strlen(s);
    memset(dp,-1,sizeof(dp));
    printf("%d\n",dfs(0,0,1));
    return 0;
}



相關文章