天天看點

計數dp-hdu-3664-Permutation Counting

題目連結:

http://acm.hdu.edu.cn/showproblem.php?pid=3664

題目意思:

求1~n的排列個數,使得逆序數(ai>i ) 為給定的k.

解題思路:

計數dp.

dp[i][j]表示前1~i的排列中,有j個數是逆序數的個數.dp[i][j]=(j+1)*dp[i-1][j]+(i-j)*dp[i-1][j-1].

考慮數i的放的位置,顯然要想得到j個逆序數,i是大于前面的,是以隻用考慮前面逆序數小于等于j的情況,而且放上這位最多隻能增加一個逆序數。如果前面有j個逆序數,将這j個數與i交換,逆序數個數不變,第i個還可以放到第i個位置,此時為(j+1)*dp[i-1][j].目前面逆序數為j-1時,此時要構造一個逆序數,可以把前面的非逆序數與i交換,這樣就多增加了一個逆序數,此時為(i-1-(j-1))*dp[i-1][j-1].

是以:dp[i][j]=(j+1)*dp[i-1][j]+(i-j)*dp[i-1][j-1].

代碼:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 1100
ll dp[Maxn][Maxn];

int main()
{
    int n,k;

    memset(dp,0,sizeof(dp));
    dp[1][0]=1; //沒有的時為順序的
    for(int i=2;i<=1000;i++)
    {
        dp[i][0]=1;
        for(int j=1;j<i;j++) //兩種情況
            dp[i][j]=((j+1)*dp[i-1][j]+(i-j)*dp[i-1][j-1])%M;
    }

    while(~scanf("%d%d",&n,&k))
    {
        if(k>=n)
        {
            printf("0\n");
            continue;
        }
        if(k==0)
        {
            printf("1\n");
            continue;
        }

        printf("%I64d\n",dp[n][k]);
    }
    return 0;
}