POJ 1037 A decorative fence

本文介绍了一种求解特定交错排列问题的方法,通过构建递推关系矩阵来高效计算满足条件的第c位排列。利用两个矩阵w[][]和m[][]分别记录不同情况下的交错排列数量,并通过递推公式进行迭代计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:有n块木板,长度分别为1,2,3,4.……,n,按照高低交错排列,即排列a1,a2,a3,......,an,任意i(1<i<n),满足a(i-1)<a(i)>a(i+1)或者a(i-1)>a(i)<a(i+1),将所有符合条件的排列按照字典序排列,求第c位的排列。

思路:

(1)设置两个数组w[][],m[][]。w[i][j]代表有i块木板,第一块的长度为j,第二块长度小于第一块长度的交错排列数目;m[i][j]代表有i块木板,第一块的长度为j,第二块长度大于第一块长度的交错排列数目。于是w[][]yum[][]满足递推关系:w[i][j]=∑m[i-1][k], k=1,2,3......,j-1;m[i][j]=∑w[i-1][k], k=j,j+1,......,i-1;

将第一个递推公式稍作转化,得:w[i][j] = ∑m[i-1][k] (k=1,2,3......,j-2) + m[i-1][j-1] = w[i][j-1] + m[i-1][j-1]   ......①

根据将每块求补(即n-ai)后同样也是符合条件的排列,得:m[i][j]=w[i][n-j+1] ......②

①②两个式子就是最后得到的递推公式。

(2)然后就是根据得到的w[][]和m[][]一步步逼近了。另外逼近得到的数字并不是真实答案,只是表示相对于前一个答案的相对位置。


#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int main()
{
    int k,n,i,j,ans[21];
    long long int c;
    long long int w[21][21],m[21][21];
    bool at_w,have_use[21];
    memset(w,0,sizeof(w));
    memset(m,0,sizeof(m));
    w[1][1]=m[1][1]=1;
    for (i=2;i<=20;i++)
        for (j=1;j<=i;j++)
        {
            w[i][j]=w[i][j-1]+m[i-1][j-1];
            m[i][i-j+1]=w[i][j];
        }
    scanf("%d",&k);
    while (k--)
    {
        scanf("%d%I64d",&n,&c);
        for (i=1,at_w=false;i<=20;i++)
        {
            if (c<=w[n][i]+m[n][i])
            {
                ans[1]=i;
                if (c<=w[n][i])
                    at_w=true;
                else
                    c-=w[n][i];
                break;
            }
            else
                c-=w[n][i]+m[n][i];
        }
        for (i=2;i<=n;i++)
        {
            if (at_w)
                for (j=1;j<ans[i-1];j++)
                    if (c<=m[n-i+1][j])
                    {
                        ans[i]=j;
                        at_w=false;
                        break;
                    }
                    else
                        c-=m[n-i+1][j];
            else
                for (j=ans[i-1];j<=n-i+1;j++)
                    if (c<=w[n-i+1][j])
                    {
                        ans[i]=j;
                        at_w=true;
                        break;
                    }
                    else
                        c-=w[n-i+1][j];
        }
        memset(have_use,false,sizeof(have_use));
        printf("%d",ans[1]);
        have_use[ans[1]]=true;
        for (i=2;i<=n;i++)
        {
            for (j=1;ans[i]>0;j++)
                if (have_use[j]==false)
                    ans[i]--;
            ans[i]=j-1;
            have_use[ans[i]]=true;
            printf(" %d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值