A decorative fence (POJ - 1037,计数类 DP)

一.题目链接:

POJ-1037

二.题目大意:

输入两个整数 n,c

要求输出字典序为 c 的 1~n 的**排列.

**排列是指长度为 n,每个数字为 1~n 且数字不重复且大小高低交错的排列.

三.分析:

可以用 “试填法” 来确定排名为 c 的排列.

具体来说,我们可以枚举第一个数的大小

当第一个数为 a[1] 时,设后面 n - 1 个数构成的**排列方案数为 t

若 t >= c,则说明第一个数为 a[1]

否则,说明第一个数应该更大,此时令 c - t,a[1] 增加 1.

重复上述过程即可确定第一个数的大小,同理确定所有数的大小.

所以我们应先预处理出 t.

f[i][j][k] 表示 i 个互异的数,其中最左边的数从小到大排序在这 i 个数中排名为 j,并且最左边的数为低(0)/高(1)位,时的方案数.

状态转移方程很简单,详见代码.

PS:在确定第一个数的大小时,应先枚举 f[n][j][1],再枚举f[n][j][1],这是因为当两者同时满足条件时,很明显 1 为高位的字典序更小.

四.代码实现:

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

typedef long long ll;

const int M = (int)20;

bool vis[M + 5];
ll f[M + 5][M + 5][2];

void init()
{
    f[1][1][0] = f[1][1][1] = 1;
    for(int i = 2; i <= M; ++i)
    {
        for(int j = 1; j <= i; ++j)
        {
            for(int k = j; k <= i - 1; ++k) f[i][j][0] += f[i - 1][k][1];
            for(int k = 1; k <= j - 1; ++k) f[i][j][1] += f[i - 1][k][0];
        }
    }
}

int main()
{
    init();
    int T;
    scanf("%d", &T);
    while(T--)
    {
        memset(vis, 0, sizeof(vis));
        int n; ll c;
        scanf("%d %lld", &n, &c);
        int ls, k;
        for(int j = 1; j <= n; ++j)
        {
            if(f[n][j][1] >= c)
            {
                ls = j;
                k = 1;
                break;
            }
            else    c -= f[n][j][1];
            if(f[n][j][0] >= c)
            {
                ls = j;
                k = 0;
                break;
            }
            else    c -= f[n][j][0];
        }
        vis[ls] = 1;
        printf("%d", ls);
        for(int i = 2; i <= n; ++i)
        {
            k ^= 1;
            int j = 0;
            for(int l = 1; l <= n; ++l)
            {
                if(vis[l])  continue;
                ++j;
                if(k == 0 && ls > l || k == 1 && ls < l)
                {
                    if(f[n - i + 1][j][k] >= c)
                    {
                        ls = l;
                        break;
                    }
                    else    c -= f[n - i + 1][j][k];
                }
            }
            vis[ls] = 1;
            printf(" %d", ls);
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/The___Flash/article/details/104827017