いつか黙示録(POJ-3208、数位DP)

1.タイトルリンク:

POJ-3208

2.トピックの主なアイデア:

悪魔の数を666を連続して含む数として定義します。

k番目に多い悪魔の数を見つけます。

3.分析:

明らかに、デジタルDPを解決するためにピット充填法を使用してください。

現在の列挙が番号のi番目の位置に到達し、i番目の位置がjであると仮定すると、現在の列挙された番号以下のデーモンの数を知る必要があります。

明らかに、f [i] [j]を前処理する必要があります。

f [i] [0]:i桁以下で、i番目の桁から6個の非悪魔番号が連続して0個ある非悪魔番号の数。

f [i] [1]:i桁以下で、i番目の桁から6が連続している非悪魔番号の数。

f [i] [2]:i桁以下の非悪魔番号と、i番目の桁から始まる2つの連続する6つの非悪魔番号の数。

f [i] [3]:i桁を超えない悪魔の数。

アクセス可能な状態遷移方程式は次のとおりです。

f [i] [0] =(f [i-1] [0] + f [i-1] [1] + f [i-1] [2])* 9

f [i] [1] = f [i-1] [0]

f [i] [2] = f [i -1] [1]

f [i] [3] = f [i-1] [2] + f [i-1] [3] * 10

詳細については、コードを参照してください。

4.コードの実装:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

ll f[25][4];

void init()
{
    f[0][0] = 1;
    for(int i = 1; i <= 20; ++i)
    {
        f[i][0] = (f[i - 1][0] + f[i - 1][1] + f[i - 1][2]) * 9;
        f[i][1] = f[i - 1][0];
        f[i][2] = f[i - 1][1];
        f[i][3] = f[i - 1][2] + f[i - 1][3] * 10;
    }
}

int main()
{
    init();
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        scanf("%d", &n);
        for(m = 1; f[m][3] < n; ++m);
        for(int i = m, k = 0; i >= 1; --i)
        {
            for(int j = 0; j <= 9; ++j)
            {
                ll cnt = f[i - 1][3];
                if(j == 6 || k == 3)
                {
                    for(int l = max(0, 3 - k - (j == 6)); l < 3; ++l)
                    {
                        cnt += f[i - 1][l];
                    }
                }
                if(cnt < n) n -= cnt;
                else
                {
                    if(k < 3)
                    {
                        if(j == 6)  ++k;
                        else        k = 0;
                    }
                    printf("%d", j);
                    break;
                }
            }
        }
        printf("\n");
    }
    return 0;
}

 

おすすめ

転載: blog.csdn.net/The___Flash/article/details/104892238