#NOIP模擬賽#排列問題(DP)




這個題,是一個DP,令人驚訝,我當時根本就沒往這方面想,還是題見得少了

同學有一個DP解法,個人感覺比標解好理解得多,具體如下:

如圖:

將數字1 ~ N從大到小填

定義Dp[full][half][sum]表示

已經填了full個格子(上下對應都填了, 如:上4下5)

有2 * half個格子填了一半(如:紅色點的兩個格子,由於這種格子必然是偶數個,所以除2)

已經填的數的總和是sum

Dp值是此時的方案數。


這題的轉移有三種情況,

以上圖爲例,現在應該填now = 3:

1,分別填在兩個藍色格子內,並使他們無法組成一個新的full,此時有N - full - (half - 1) * 2個上下同時爲空的格子,所以:

Dp[full][half][sum] += Dp[full][half - 1][sum - 2 * now] * (N - full - (half - 1) * 2) * (N - full - (half - 1) * 2 - 1) % MOD;


2,使成爲一個新的full(直接填在一對對應的空格子裏(藍色), 或者填一個在紅色格子裏,再填一個在藍色格子裏,half值不變,將損失1對full):

Dp[full][half][sum] += Dp[full - 1][half][sum - now] * (half  * 2 * (N - half *2 - (full - 1)) + N - half * 2 - (full - 1));


3,使成爲一個新的full(填在兩個半格里(紅色),將損失1對half):

Dp[full][half][sum] += Dp[full - 2][half + 1][sum] * (half + 1) * (half + 1);


最後的答案是 Σ Dp[N][0][sum](sum >= K)


下面是標解,和上面的解法有所不同:



Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

const int MOD = 1e9 + 7;

int N, K, L, Ans;
int Dp[55][55][55 * 55];

bool getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9'){
        if(c == '-')    flg = -1;
        if(c == -1) return 0;
    }
    while(c >= '0' && c <= '9'){
        num = num * 10 + c - 48;
        if((c = getchar()) == -1)   return 0;
    }
    num *= flg;
    return 1;
}

int P(int x){
    int rt = 1;
    for(int i = 2; i <= x; ++ i)
        rt = 1ll * rt * i % MOD;
    return rt;
}

int main(){
    freopen("data1.in", "r", stdin);
    //freopen("permutation.in", "r", stdin);
    //freopen("permutation.out", "w", stdout);
    getint(N), getint(K);
    int up = N * N;
    Dp[0][0][0] = 1;
    for(int i = 1; i <= N; ++ i){
        int now = N - i + 1;
        for(int full = 0; full <= i; ++ full){
            int half = i - full;
            for(int s = 0; s <= up; ++ s){
                if(s >= now * 2 && N - full - (half - 1) * 2 > 0)
                    Dp[full][half][s] = (Dp[full][half][s] + 1ll*Dp[full][half-1][s-(now << 1)]*(N-full-((half-1)<<1))%MOD*(N-full-((half-1)<<1)-1)%MOD)%MOD;
                if(full && s >= now)
                    Dp[full][half][s] = (Dp[full][half][s] + 1ll*Dp[full-1][half][s-now]*((half*(N-(half<<1)-(full-1))<<1)%MOD+N-(half<<1)-(full-1))%MOD)%MOD;
                if(full >= 2)
                    Dp[full][half][s] = (Dp[full][half][s] + 1ll*Dp[full-2][half+1][s]*(half+1)%MOD*(half+1)%MOD)%MOD;
            }
        }
    }
    int Ans = 0;
    for(int i = K; i <= up; ++ i)
        Ans = (Ans + Dp[N][0][i]) % MOD;
    printf("%d\n", Ans);
    return 0;
}





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