poj——A decorative fence

题意:有n个1xi的木棒(i从1到n)。把它们排成一排,要求为“波浪形”,即对于第i个木棒要满足a[i-1]<a[i]、a[i]>a[i+1]    或者a[i-1]>a[i]、a[i]<a[i+1]。将这写排列按字典序排序后输出第k个排列。


分析:首先dp出以i(1~n)开头的满足要求的序列有几个,分为2种情况:“121”型和“212”型;然后可以求出k处于以哪    个数字开头的哪种类型中,在从第2位开始以相同的方法计算直到第n位。

    dp[i][len][0]表示以i开头长度为len且为“121”型的排列数目,则:dp[i][len][0]=sum(dp[j][len-1][1])(j从i到len-1);

    dp[i][len][1]表示以i开头长度为len且为“212”型的排列数目,则:dp[i][len][1]=sum(dp[j][len-1][0])(j从1到i);


    由于每次存储i的都是从之前的比如12345种选出3后,剩下的1245对应的1234这种所确定的,所以要输出返回映     射为1~n的数。

#include<cstdio>
#include<cstring>
long long int m,dp[21][21][2];
void get_dp()
{
	int i,j,k;
	memset(dp,0,sizeof(dp));
	dp[1][1][0]=dp[1][1][1]=1;
	for(j=2; j<=20; j++)
        for(i=1; i<=j; i++)
        {
            for(k=i; k<j; k++)
                dp[i][j][0]+=dp[k][j-1][1];
            for(k=1; k<i; k++)
                dp[i][j][1]+=dp[k][j-1][0];
        }
}
int main()
{
	bool flag,used[21];
	int T,n,i,j,k,ans[21];
	get_dp();
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%lld",&n,&m);
    	long long sum=0;
    	int tmp,len=2,tn=n;
		for(i=1; i<=tn; i++)
    	{
        	if(sum+dp[i][tn][0]+dp[i][tn][1]>=m)
        	{
            	ans[1]=i;
            	tmp=i;
            	m-=sum;
            	break;
        	}
        	sum+=(dp[i][tn][0]+dp[i][tn][1]);
    	}
    	
    	if(m<=dp[tmp][tn][1])flag=true;
    	else
    	{
        	m-=dp[tmp][tn][1];
        	flag=false;
    	}
    	tn--;
    	while(tn>0)
    	{
        	if(flag)
        	{
            	for(i=1; i<tmp; i++)
            	{
                	if(dp[i][tn][0]>=m)
                	{
                    	tmp=i;
                    	ans[len++]=i;
                    	break;
                	}
                	m-=dp[i][tn][0];
            	}
        	}
        	else
        	{
            	for(i=tmp; i<=tn; i++)
            	{
                	if(dp[i][tn][1]>=m)
                	{
                    	tmp=i;
                    	ans[len++]=i;
                    	break;
                	}
                	m-=dp[i][tn][1];
            	}
        	}
        	tn--;
        	flag=!flag;
    	}
    	memset(used,0,sizeof(used));
    	for(i=1; i<=n; i++)
        	for(j=1; j<=n; j++)
            	if(!used[j])
            	{
                	ans[i]--;
                	if(ans[i]==0)
                	{
                    	printf("%d ",j);
                   		used[j]=true;
                   		break;
                	}
            	}
    	printf("\n");
    }
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值