Two Arrays CodeForces - 1288C

题面:https://vjudge.net/problem/CodeForces-1288C

这道题其实比较难想,据说题解一种方法是组合方法(然而我不会),我来总结总结dp的方法吧。

首先我们采用一个dp数组,dp[i][j][k]:在生成的a,b字符串中,第k个的数字分别是i,j,在满足这种情况下有多少种可能。

众所周知j一定要大于i,当k为1的时候满足j>i的情况下都只有一种可能(因为就一个数字)所以我们遍历的时候就要这样:

1
2
3
for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++)
            dp[i][j][1] = 1;

接下来考虑递推关系式,我们知道dp[i][j][k]一定是从dp[x][y][k - 1]拓展出来的,我们不妨考虑考虑,当x <= i && y >= j时dp[x][y][k - 1]一定能拓展到dp[i][j][k],因为i代表a字符串第k位为i,按照题意i应该大于之前的所有,所以在k之前的x都要小于等于i,j也是一样的,但是要注意,j是递减的,所以在k之前的y都要大于等于j,并且小于题目限定的n。

我们就知道了,dp[i][j][k] = ∑(1,i)∑(j,n)dp[i][j][k - 1];

这个怎么算呢?

我们可以采用前缀和的方法处理,我们假设sum[i][j] = ∑(1,i)∑(j,n)dp[i][j][k - 1], 先计算对于每个i的所有j的和,然后再计算从1到i的和即可得到sum[i][j],而且,如果我们得到了每个i的所有j的和(复杂度O(n2)),再得到sum[i][j]就可以从sum[i - 1][j] + 第i行所有j之和得到。

我们就把sum用两次,第一次计算每行的j之和,易得代码:

1
2
3
for(int i = 1; i <= n; i++)
            for(int j = n; j >= i; j--)
                sum[i][j] = (sum[i][j + 1] + dp[i][j][k - 1]) % mod;

然后第二次计算从第1行到达第i行的和,按照上上段的方法:

1
2
3
for(int i = 1; i <= n; i++)
            for(int j = n; j >= i; j--)
                sum[i][j] = (sum[i][j] + sum[i - 1][j]) % mod;

注意,因为当j<i时毫无意义,所以直接不遍历这种情况。

最后得到的dp[i][j][k]对于每个i和j的和即为答案,我们可以用类似的方法解决。

注意点:每次采用sum时记得清零。

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000000000 + 7;
int dp[1005][1005][15];
int sum[1005][1005];
int main()
{
    int n, m, ans = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        for(int j = i; j <= n; j++)
            dp[i][j][1] = 1;
    for(int k = 2; k <= m; k++)
    {
        memset(sum, 0, sizeof(sum));//清零sum
        for(int i = 1; i <= n; i++)
            for(int j = n; j >= i; j--)
                sum[i][j] = (sum[i][j + 1] + dp[i][j][k - 1]) % mod;//计算dp[i][j][k - 1]对于每个i的所有j之和
        for(int i = 1; i <= n; i++)
            for(int j = n; j >= i; j--)//统计从1到i的每行和之和
                sum[i][j] = (sum[i][j] + sum[i - 1][j]) % mod;
        for(int i = 1; i <= n; i++)
            for(int j = i; j <= n; j++)
                dp[i][j][k] = sum[i][j];
    }
    memset(sum, 0, sizeof(sum));
    for(int i = 1; i <= n; i++)
        for(int j = n; j >= i; j--)
            sum[i][j] = (sum[i][j + 1] + dp[i][j][m]) % mod;
    for(int i = 1; i <= n; i++)
        ans = (ans + sum[i][i]) % mod;
    printf("%d\n", ans);
    return 0;
}

  

posted @   funforever  阅读(137)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示