您的位置:首页 > 其它

#NOIP模拟赛#排列问题(DP)

2017-08-19 21:30 274 查看






这个题,是一个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
[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
[0][i]) % MOD;
printf("%d\n", Ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: