《算法进阶指南》--计数类DP--装饰围栏

计数类DP通常对不从不漏的要求严格,一般围绕一个基本点展开。这题的基本点在于,当前i个木板确定,后面剩余的木板的计数依旧符合n个木板的计数方法,需要枚举第一个数及其属于高位或低位进行转移。用f[i][j][k]表示长度为i,第一个数为第j大,处于k0高位或k1低位的数量。

int f[22][22][2];

void init()
{
    f[1][1][0] = 1, f[1][1][1] = 1;
    for (int i = 2; i <= 20; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            for (int k = 0; k <= 1; k++)
            {
                if (k == 0)
                {
                    for (int jj = j; jj < i; jj++)
                    {
                        f[i][j][k] += f[i - 1][jj][k ^ 1];
                    }
                }
                else
                {
                    for (int jj = 1; jj < j; jj++)
                    {
                        f[i][j][k] += f[i - 1][jj][k ^ 1];
                    }
                }
            }
        }
    }
}
int ans[22];
int vis[22] = {0};
void solve()
{
    init();
    int q;
    cin >> q;
    int n, c;

    while (q--)
    {
        memset(vis, 0, sizeof(vis));
        cin >> n >> c;
        int lastlen, lastk;
        for (int j = 1; j <= n; j++)
        {
            int ok = 0;
            for (int k = 1; k >= 0; k--)
            {
                if (f[n][j][k] >= c)
                {
                    lastlen = j;
                    lastk = k;
                    ok = 1;
                    break;
                }
                else
                {
                    c -= f[n][j][k];
                }
            }
            if (ok == 1)
            {
                break;
            }
        }
        cout << lastlen << ' ';
        vis[lastlen] = 1;
        for (int i = 2; i <= n; i++)
        {
            int j = 0;
            for (int len = 1; len <= n; len++)
            {
                if (vis[len])
                {
                    continue;
                }
                j++;
                if (lastk == 0 && len > lastlen || lastk == 1 && len < lastlen)
                {
                    if (f[n - i + 1][j][lastk ^ 1] >= c)
                    {
                        lastlen = len;
                        lastk ^= 1;
                        cout << len << ' ';
                        break;
                    }
                    else
                    {
                        c -= f[n - i + 1][j][lastk ^ 1];
                    }
                }
            }
        }

        cout << endl;
    }
}

收获:
对于求第c个大的具体方案,c为一个极大数。通常用倍增的思想不断逼近c。

posted @ 2025-07-17 15:52  青一凡  阅读(13)  评论(0)    收藏  举报