51Nod 1296 构造排列 + DP

题目链接


题意:
给定N,要求构造满足要求的排列,对于其中的一些成员,值大于左右邻居,对于另一些成员,值小于左右邻居。输出满足条件的排列种数。


思路:
d p [ i ] [ j ] 为前 i 个数构成的满足条件的合法排列,且末尾为 j 的个数。
那么对于第 i 个数,我们只需要考虑其与左邻居的关系(第i个数与右邻居的关系等价于第(i+1)个数与左邻居的关系)
构建一个数组 a
如果第 i 个数大于左邻居,则 a [ i ] = 1
如果第 i 个数小于左邻居,则 a [ i ] = 0
如果第 i 个数等于左邻居,则 a [ i ] = 1

另外还需要考虑的一个问题是,当我们在考虑前 i 个数组成的排列且末尾为 j 时,我们如何通过 d p [ i 1 ] 的值来快速推出 d p [ i ] 的值,因为当 j < i 时,对于 d p [ i 1 ] 数组中的合法排列, j 是有可能已经出现在这些排列的中间,如果再在末尾插入 j ,就出现了排列中有两个 j 的矛盾。

那应该如何来解决这个问题呢?此时有一个非常巧妙也非常经典的构建新排列的方式,当我们在一个排列的末尾插入 j 时,为避免冲突,我们将原排列的所有不小于 j 的数通通 + 1 ,这样的好处时,既不破坏原排列相邻数之间的大小关系,同时也避免了两个 j 出现的可能。

故此时就可以利用排列的构造方法来求得DP的状态转移方程了:
a [ i ] = 1 d p [ i ] [ j ] = k = 1 j 1 d p [ i 1 ] [ k ]
a [ i ] = 0 d p [ i ] [ j ] = k = j i 1 d p [ i 1 ] [ k ]
a [ i ] = 1 d p [ i ] [ j ] = k = 1 i 1 d p [ i 1 ] [ k ]

此题得解。

代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;

const int mod = 1e9 + 7;
const int A = 5000 + 10;
int dp[A],sum[2][A],a[A];

int main(){
    int N,K,L;
    scanf("%d%d%d",&N,&K,&L);

    memset(a,-1,sizeof(a));
    bool flag = 1;
    for(int i=1 ;i<=K ;i++){
        int x;scanf("%d",&x);
        x++;
        a[x] = 0;a[x+1] = 1;
    }
    for(int i=1 ;i<=L ;i++){
        int x;scanf("%d",&x);
        x++;
        if(a[x] == 0 || a[x+1] == 1){
            flag = 0;
            break;
        }
        a[x] = 1;a[x+1] = 0;
    }

    if(flag == 0) puts("0");
    else{
        sum[0][0] = 1;

        for(int i=1 ;i<=N ;i++){
            sum[i&1][0] = 0;
            for(int j=1 ;j<=i ;j++){
                if(a[i] == -1) dp[j] = sum[(i&1)^1][i-1];
                else if(a[i] == 1) dp[j] = sum[(i&1)^1][j-1];
                else               dp[j] = (sum[(i&1)^1][i-1] - sum[(i&1)^1][j-1])%mod;
                sum[i&1][j] = (sum[i&1][j-1] + dp[j]) % mod;
            }
        }
        ll ans = 0;
        for(int i=1 ;i<=N ;i++){
            ans = (ans + dp[i])%mod;
        }
        if(ans < 0) ans += mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wubaizhe/article/details/81038060
今日推荐