1. 程式人生 > >【dp】51nod

【dp】51nod

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

/*
51nod - 1020 逆序排列
對於一個有n個數的序列,是由有n-1個數的序列再添上一個n組成,n添在哪個部位,就決定了狀態的轉移。
dp[i][j] 代表有i個數的序列,逆序列值為j的個數。
dp[i][j] = sum(dp[i-1][(j-i+1)~j]);
如此來三層for,自然轉換一下,記錄上個狀態的字首和,本來想開個pre[1010][20004]的陣列,既然開不了,那麼就換成滾動陣列
就好了,反正不需要記錄這個過程的值。

參閱了其他coder的程式碼,其實遞推式可以進一步推:
dp[i][j] = sum(dp[i-1][(j-i+1)~j]);
dp[i][j-1] = sum(dp[i-1][(j-i)~(j-1)]);
兩式相減:dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-i];
*/

// code 1
const int mod = 1e9+7;
int dp[1010][20004];
int pre[2][20004];
int main(){
    memset(dp,0,sizeof(dp));
    dp[1][0] = 1;
    for(int j = 0; j<=20000; j++)
        pre[1][j] = 1;
    for(int i = 2; i<=1000; i++){
        for(int j = 0; j<=20000; j++){
            int now = i%2;
            int pos = (i-1)%2;
            dp[i][j] = (pre[pos][j]-(j-i<=0?0:pre[pos][j-i]))%mod;
            if(!j)  pre[now][j] = dp[i][j];
            else    pre[now][j] = (pre[now][j-1]+dp[i][j])%mod;
        }
    }

    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        printf("%d\n",(dp[n][k]+mod)%mod);
    }
    return 0;
}

//  code 2
const int mod = 1e9+7;
int dp[1010][20004];
int main(){
    memset(dp,0,sizeof(dp));
    for(int i = 1; i <= 1000; i++){
        dp[i][0] = 1;
        for(int j = 1; j<=(i*(i-1))/2 && j<=20000; j++){
            if(j >= i)
                dp[i][j] = ((dp[i][j-1]+dp[i-1][j]-dp[i-1][j-i])%mod + mod)%mod;
            else
                dp[i][j] = (dp[i][j-1]+dp[i-1][j])%mod;
            //printf("%d %d %d\n",i,j,dp[i][j]);
        }
    }
    int T;
    scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        printf("%d\n",(dp[n][k]+mod)%mod);
    }
    return 0;
}