haraduka's diary

やる気が欲しい

ABC029 D問題 1

久しぶりに暇だったのでやってみた。
桁DPなるものを初めてやったのでメモ

digitDPはこのサイトが驚くほどわかりやすいtatanaideyo.hatenablog.com

考え方はいわゆるdigitDP
dpの配列を、dp[何桁目を見ているか][今見ている桁までで1が何回出ているか(桁の数以下)][その数はNより小さいか]というようにとる。
何桁目を見ているかは、0桁目をlen(N)+1桁目の仮想的なものとし、上から1,2桁と数える。
1が何回出ているか、というのをまとめるところに今回の数をまとめるdp要素がある。
その数より小さいかは、0ならギリギリ、1なら余裕を表す。
あとはdpを回すだけ。

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    int N; cin >> N;
    string sN = to_string(N);

    //digitDP
    // keta, 1num, is smaller than N
    int dp[11][11][2];
    memset(dp, 0, sizeof(dp));
    dp[0][0][0] = 1;

    for(int keta=1; keta<=len(sN); keta++){
        for(int num=0; num<11; num++){
            for(int is=0; is<2; is++){
                if(is == 0){ //no
                    int mx = sN[keta-1]-'0';
                    for(int i=0; i<mx; i++){
                        if(i == 1){
                            dp[keta][num+1][1] += dp[keta-1][num][is];
                        }else{
                            dp[keta][num][1] += dp[keta-1][num][is];
                        }
                    }
                    if(mx == 1){
                        dp[keta][num+1][is] += dp[keta-1][num][is];
                    }else{
                        dp[keta][num][is] += dp[keta-1][num][is];
                    }
                }else if(is == 1){ //ok
                    for(int i=0; i<10; i++){
                        if(i == 1){
                            dp[keta][num+1][is] += dp[keta-1][num][is];
                        }else{
                            dp[keta][num][is] += dp[keta-1][num][is];
                        }
                    }
                }
            }
        }
    }
    int res = 0;
    for(int i=0; i<11; i++){
        for(int j=0; j<2; j++){
            res += i*dp[len(sN)][i][j];
        }
    }
    cout << res << endl;
}