1. 程式人生 > >51nod:數字1的數量(線性dp or 數位dp)

51nod:數字1的數量(線性dp or 數位dp)

Input
輸入N(1 <= N <= 10^9)
Output
輸出包含1的個數
Input示例
12
Output示例

5

題目思路:

這道題資料這麼大顯然不能直接求得,想的時候一下子就想出來dp的方法(不過轉移狀態的時候,有個·地方是減一還是加一一直沒搞清楚,想了挺久才搞清楚),但是沒反映出來是dp,可能只是用到dp儲存之前節點的值的概念,不過,沒關係能想到dp的方法就好了,首先我們為什麼會想到用dp,因為對於一個數,高位的數字1的數量是乘以低位數字的數量,比如538,加入前兩位的數字1的數量是35,那麼高位的數字1的數量就5*35了,這個很容易想到吧,然後我們自然就知道是用dp了,這樣我們就很容易定義出狀態dp[i]為i位數1的數量那麼答案就是所有的dp[i]相加了,雖然狀態很容易定義出來,狀態轉移也不難想出來,但是有一點細節需要考慮,就是我們轉移狀態的時候分為等於1和大於1的情況,大於一的時候我們是轉移x*num[i-1]還是(x-1)num[i-1]呢一開始我以為是後者,wa的天昏地暗,腦子不夠清醒,忘記自己的狀態定義的是什麼了,我們狀態定義的是第i位數一的數量 比如321 dp[1]就是1~1的1的數量,dp【2】就是10~21的數量,dp【3】就是100~321的數量,那麼這樣的話我們就知道是x-1還是x了,顯然是x的,因為這裡dp【i-1】有一部分沒有算到,就是21~100的部分,然後對於第i為300~321的也不好算,但是剛好和前面那部分相加起來就是一個num【i-1】所以是x,另外還要注意當前位是1的情況

num【i】代表1~1^i的一的數量

ac程式碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<sstream>
#include<map>
#include<cmath>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
int dp[20];
int num[20];
string s;
int main()
{
    while(cin>>s)
    {
        int len = s.size();
        reverse(s.begin(),s.end());
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        int last = 1;
        for(int i = 0;i<len;i++ )
        {
            if(i==0)
            {

                dp[i] = s[i]=='0'?0:1;
                num[i] = 1;
            }
            else
            {
                num[i] = num[i-1]*10+pow(10,i);
                if(s[i]>'1')
                {
                    dp[i] = (s[i]-'0')*num[i-1]+pow(10,i);
                }
                else if(s[i] == '1')
                {
                    int tmp = 0;
                    for(int j = i-1 ;j>=0;j--)
                        tmp = tmp*10+(s[j]-'0');

                    dp[i] = num[i-1]+1+tmp;
                }
            }
        }
        int ans = 0;
        for(int i = 0 ;i<len;i++)
        {
           // cout<<dp[i]<<endl;
            ans+=dp[i];
        }
        cout<<ans<<endl;
    }
}