POJ 1037 A decorative fence 动态规划

POJ 1037 Adecorative fence 动态规划

题意,给一个数字J,要求1-J中的数字进行交错排列,即每个数的左右两边的数应比它大或比它小,并且将所有排列进行字典序排序后,查找第C个排列。

步骤一:枚举预处理

首先,交错排列无非就是两种,一种是,第一数比第二个数大,即W型,一种是第一个数比第二个数小,即M型。我们可以定义两个数组,分别记录以i开头,总数为j根的排列的个数。

 

 

up[i][j]表示以j开头总数为i根的排列个数,同时第一根比第二根矮,即M型。

down[i][j]表示以j开头总数为i根的排列个数,同事第一根比第二根高,即W型。

 

很容易的想到对于W型排列,如果想要以j开头并且总根数为i根,那么我们可以把以比j小的数字开头的并且总根数为i-1的M型排列接在j后面如下图。而对于M型略有不同,详情看注释。。



这样就可以得到down[i][j]= ∑(up[i-1][k]) ,k∈[1,j-1];

                       up[i][j]= ∑(down[i-1][k]) ,k∈[j,i-1];

注:在写题解的过程中,又出现了两个比较纠结的地方,就是当为什么k必须小于i-1,(废话,因为k是属于i-1根,最大只能到i-1啊),另外有一个就是为什么k可以等于j,这个写程序的时候没有发现,但是写题解才注意到,仔细想发现对于一个这样的过程,是把这个j放到了后面,或者中间,例如,对于up[2][1]+=down[1][1] 则是1,2排列把2放在了后面,但是这样还是不对,后来又发现,对于任意一个j<=k的情况都不是单纯地把一个排列接在另一个排列后面,因为j在i-1的情况里已经出现过,所以仔细一想觉得是把i放在了i-1的情况的第一位后也就是第二位,因为在i-1中并没有出现过i,而因为是正向枚举,所以i肯定比i-1中任意一个数都要大因此如果要在原本的W型排列中出现升序,一定是将i放在了第二位这样就能保证它一定比第一位和第三位要大,这样才能成立。

 

步骤二:查找第c个排列

按开头顺序枚举下来,肯定是按字典序排好的,既然我们用up,和down数组已经求出了各种开头排列方式的数量,那么就可以逐个减去这些开头以求得第c各排列在哪个区间内。

具体我们可以先对于第一个位置进行确定,方法嘛就是对于第一个位置进行枚举j表示当前木板,则对于第一个位置,j∈[1,N],因为是第一个位置所以我们只需要逐个求和Temp+=up[N][j]+down[N][j]并判断Temp是否大于C,如果大于,则当位的j就是我们要找的第一根木板,并且用一个vis数组来记录这个木板是否访问过以避免后面出现重复计算,同时C-=temp。

接着再对第二个位置进行确定,这里于第一根的确定方法不太相同,因为是fence是交错的所以我们需要先确定第二个位置是出于up序列还是down序列,判断后同样用j往下枚举并且每一步都判断处于up还是down序列并且减去相应的排列数以判断是否超过C,判断方法与第一根木板相同。(注:这些都是对于同一位置的枚举,不要误解为一位一位找下去)

接下来就是每一个木板的确认啦,方法嘛与第二步相同。

最坏的时间就是((n+1)*n/2)啦。。一个等差数列。

 

下面是代码:


#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <stack>
#include <cmath>
#include <map>
#include <set>
#include <algorithm>
#include <cctype>
#include <vector>
#include <ctime>
#include <list>
#include <deque>
#include <bitset>
#define MEM(a,x) memset(a,x,sizeof(a));
#define MEMINF(a) memset(a,0x3f,sizeof(a));
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)<(b)?(a):(b)
#define FL(s,n,a) fill(s,s+n,a);
#define MAPIT(a,b) map<a,b>::iterator it;
#define VECIT(type) vector<type>::iterator it;
#define SETIT(type) set<type>::iterator it;
using namespace std;
typedef long long ll;
const int MAXN=22;
ll up[MAXN][MAXN],down[MAXN][MAXN],Find[MAXN],C;
int vis[MAXN],T,N;
void init()
{
        MEM(up,0);
        MEM(down,0);
        up[1][1]=down[1][1]=1;
        for(int i=2;i<=5;i++)
           for(int j=1;j<=i;j++)
            {
                for(int k=1;k<=j-1;k++)down[i][j]+=up[i-1][k];
                for(int k=j;k<=i-1;k++)up[i][j]+=down[i-1][k];
            }
}
void findc()
{
    int cnt;
    MEM(vis,false);
    MEM(Find,0);
    ll l=0,temp;
    for(int i=1;i<=N;i++)
    {
        int j;
        cnt=0;
        for(j=1;j<=N;j++)
        {
           temp=C;
           /**记录下当前C的大小,每次循环减去排列数后都不一样,当找
           到当前需要的木板后,C是多减了一组排列数的,所以下面需要一个回到
           上一个状态的过程,**/
           if(!vis[j])
           {
               cnt++;//把j当做剩下木棒里的第cnt短。
               if(i==1)
                C-=(up[N][cnt]+down[N][cnt]);
               else if(j>=Find[i-1]&&(i==2||Find[i-2]>Find[i-1]))
                C-=down[N-i+1][cnt];
               else if(j<Find[i-1]&&(i==2||Find[i-2]<Find[i-1]))
                C-=up[N-i+1][cnt];
               if(C<=0) break;
           }
        }
        vis[j]=true;//记录j已经访问,减少重复计算。
        Find[i]=j;//确定当前位置是j木板
        C=temp;
    }
    printf("%d",Find[1]);
    for(int i=2;i<=N;i++) printf(" %d",Find[i]);
    puts("");
}
int  main()
{
    init();
    cin>>T;
    while(T--){
        scanf("%d %I64d",&N,&C);
        findc();
    }
}


  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值