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();
    }
}


發佈了35 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章