【Gym - 102428F】Fabricating Sculptures (单峰计数dp)

题意

输入 n , m n,m n,m,求底座长度为 n n n,方块个数为 m m m的不会发生积水的雕像的个数,答案模 1 0 9 + 7 10^9+7 109+7。( n , m ≤ 5000 n,m\le 5000 n,m5000
【Gym - 102428F】Fabricating Sculptures (单峰计数dp)_第1张图片
n = 3 , m = 6 n=3,m=6 n=3,m=6,左边为合法的方案,右边不合法。

题解

可以转化为要求方块要单峰。
通过这一题发现,单峰的计数问题可以从高度的层面入手考虑。因为每一层的方块个数是单调的,而且一定在上一层放置的方块上面。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示已经放了 i i i个方块,当前行的还有 n − j n-j nj个方块的方案数。
d p [ i ] [ j ] = ∑ k = 0 j d p [ i − ( n − j ) ] [ k ] ∗ ( j − k + 1 ) dp[i][j]=\sum_{k=0}^{j}dp[i-(n-j)][k] * (j-k+1) dp[i][j]=k=0jdp[i(nj)][k](jk+1)
前缀和优化一下即可 O ( n 2 ) O(n^2) O(n2)解决。

#include 

using namespace std;
typedef long long ll;
const int maxn = 5010;

ll dp[maxn][maxn], f[maxn][maxn], g[maxn][maxn];

const ll mod = 1e9 + 7;

int main() {
    int n, m;
    cin >> n >> m;
    dp[n][0] = 1;
    for(int i = 0; i <= n; i ++) {
        f[n][i] = f[n][i-1] + dp[n][i];
        g[n][i] = g[n][i-1] + dp[n][i] * (1 - i);
    }
    for(int i = n+1; i <= m; i ++){
        for(int j = 0; j <= n; j ++){
            if(i+j-n >= 0) dp[i][j] = (j * f[i+j-n][j] % mod + g[i+j-n][j]) % mod;
        }
        for(int j = 0; j <= n; j ++){
            f[i][j] = (f[i][j-1] + dp[i][j]) % mod;
            g[i][j] = (g[i][j-1] + dp[i][j] * (1 - j) % mod) % mod;
        }
    }
    ll res = 0;
    for(int i = 0; i <= n; i ++) res = (res + dp[m][i]) % mod;
    cout << (res + mod) % mod << endl;
    return 0;
}

你可能感兴趣的:(动态规划,动态规划,题解)