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的情况下都只有一种可能(因为就一个数字)所以我们遍历的时候就要这样:
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之和,易得代码:
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行的和,按照上上段的方法:
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代码:
#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; }