DP - 數位DP - 計數問題 + UVA 12670 - Counting ones

DP - 數位DP - 計數問題 + UVA 12670 - Counting ones


1、計數問題

給定兩個整數 a 和 b,求 a 和 b 之間的所有數字中0~9的出現次數。

例如,a=1024,b=1032,則 a 和 b 之間共有9個數如下:

1024 1025 1026 1027 1028 1029 1030 1031 1032

其中‘0’出現10次,‘1’出現10次,‘2’出現7次,‘3’出現3次等等…

輸入格式
輸入包含多組測試數據。

每組測試數據佔一行,包含兩個整數 a 和 b。

當讀入一行爲0 0時,表示輸入終止,且該行不作處理。

輸出格式
每組數據輸出一個結果,每個結果佔一行。

每個結果包含十個用空格隔開的數字,第一個數字表示‘0’出現的次數,第二個數字表示‘1’出現的次數,以此類推。

數據範圍
0<a,b<100000000

輸入樣例:
1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0 

輸出樣例:
1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
115 666 215 215 214 205 205 154 105 106
16 113 19 20 114 20 20 19 19 16
107 105 100 101 101 197 200 200 200 200
413 1133 503 503 503 502 502 417 402 412
196 512 186 104 87 93 97 97 142 196
398 1375 398 398 405 499 499 495 488 471
294 1256 296 296 296 296 287 286 286 247

分析:

1按數位的長度來分析,計算1~N[a,b]N內所有數的各個數字出現的次數。\\再利用前綴和思想來求給定區間[a,b]中,各數字出現的次數。

示例:
7N=abcdefg[1,abcdefg]以長度爲7的數N=abcdefg爲慄,計算區間[1,abcdefg]中各數字出現的次數:

4x[001,abc1]efg[000,999]N_ _ _ x _ _ _(abc1)×1000假設當前考慮第4位數取x:\\①、前三位數取區間[001,abc-1]中的任意數。此時,無論efg三位取何值[000,999],均不會超過N,\\\qquad根據乘法原理,形如\_\ \_\ \_\ x\ \_\ \_\ \_的數字共有(abc-1)×1000種選擇方案。

注意: 0001本題爲避免重複計算,前三位不能全0,因此從001開始纔是合法方案。

abc3x>dabcx_ _ _>abcdefg0②、前三位數取abc,此時又可分爲3種情況:\\\qquadⅠ、x>d,此時abcx\_\ \_\ \_>abcdefg,故總方案數爲0。

x=d[000,efg]efg+1\\\qquadⅡ、x=d,此時後三位的取值範圍爲[000,efg],總方案數爲efg+1。

x<d[000,999]1000\\\qquadⅢ、x<d,此時後三位的取值範圍爲[000,999],總方案數爲1000。

+方案總數及①+②的總數。

總結一般規律:

sN=an1an2...a0Niaiii1Ni1:S=an1an2...ai+1ii(S1)×10i對長度爲s的數字,設N=a_{n-1}a_{n-2}...a_0,從後往前,N的第i位是a_i,考慮第i位:\\①、前i-1位數小於N的前i-1位數:記S=a_{n-1}a_{n-2}...a_{i+1},第i位以後共有i位數,則總方案數爲(S-1)×10^i。

i1Ni:iaiai1ai2...a0+1②、前i-1位與N的前i位數相同:\\\qquadⅠ、若第i位數恰好取a_i,則總方案數爲a_{i-1}a_{i-2}...a_{0}+1。

iai10i\\\qquadⅡ、若第i位數小於a_i,則總方案數爲10^i。

具體落實:

Count1函數Count計算1~NN()N中各數字出現的次數。把N的每一位數取出存入數組中,\\從後往前(從高位到低位)依次討論每一位取值對應的方案總數。

注意:

00最高位不能取0,因此當統計0出現的次數時,從次高位開始遍歷。

代碼:

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int get(vector<int> t,int l,int r)
{
    int res=0;
    for(int i=l;i>=r;i--)
        res=res*10+t[i];
    return res;
}

int power10(int x)
{
    int res=1;
    while(x--) res*=10;
    return res;
}

int Count(int n,int x)
{
    if(!n) return 0;
    
    vector<int> V;
    while(n) V.push_back(n%10),n/=10;
    
    int res=0,s=V.size();
    for(int i=s-1-(!x);i>=0;i--)
    {
        if(i<s-1)
        {
            res+=get(V,s-1,i+1)*power10(i);
            if(!x) res-=power10(i);
        }
        
        if(V[i]==x) res+=get(V,i-1,0)+1;
        else if(V[i]>x) res+=power10(i);
    }

    return res;
}

int main()
{
    int l,r;
    while(cin>>l>>r,l||r)
    {
        if(l>r) swap(l,r);
        
        for(int i=0;i<=9;i++)
            cout<<Count(r,i)-Count(l-1,i)<<' ';
        cout<<endl;
    }
    
    return 0;
}

2、UVA 12670 - Counting ones

題意:

[A,B]1給定區間[A,B],要求區間中的所有數在二進制表示下,'1'出現的次數總和。

數據範圍:

1AB10161 ≤ A ≤ B ≤ 10^{16}

Sample Input:
1000000000000000 10000000000000000
2 12
9007199254740992 9007199254740992

Sample Output:
239502115812196372
21
1

分析:

1與第一題類似,本題只不過是求一個二進制數的'1'出現的次數。

注意:

1long long由於本題數據範圍較大,做移位運算時,要將整數1強制轉化爲long\ long型。

代碼:

#include<iostream>
#include<vector>
#include<algorithm>

#define ll long long

using namespace std;

ll get(vector<int> t,ll l,ll r)
{
    ll res=0;
    for(int i=l;i>=r;i--)
        res=(res<<1)+t[i];
    return res;
}

ll Count(ll n)
{
    if(!n) return 0;
    
    vector<int> V;
    while(n) V.push_back(n%2),n/=2;
    
    ll res=0,s=V.size();
    for(ll i=s-1;i>=0;i--)
    {
        if(i<s-1) res+=get(V,s-1,i+1)*(1ll<<i);
        
        if(V[i]==1) res+=get(V,i-1,0)+1;
    }

    return res;
}

int main()
{
    ll l,r;
    while(cin>>l>>r)
        cout<<Count(r)-Count(l-1)<<endl;
    
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章