杨老师的照相排列
输入样例:
1
30
5
1 1 1 1 1
3
3 2 1
4
5 3 3 1
5
6 5 4 3 2
2
15 15
0
输出样例:
1
1
16
4158
141892608
9694845
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 31;
int n;
LL f[N][N][N][N][N];
int main()
{
while (cin >> n, n)
{
int s[5] = {0};
for (int i = 0; i < n; i ++ ) cin >> s[i];
memset(f, 0, sizeof f);
f[0][0][0][0][0] = 1;
for (int a = 0; a <= s[0]; a ++ )
for (int b = 0; b <= min(a, s[1]); b ++ )
for (int c = 0; c <= min(b, s[2]); c ++ )
for (int d = 0; d <= min(c, s[3]); d ++ )
for (int e = 0; e <= min(d, s[4]); e ++ )
{
LL &x = f[a][b][c][d][e];
if (a && a - 1 >= b) x += f[a - 1][b][c][d][e];
if (b && b - 1 >= c) x += f[a][b - 1][c][d][e];
if (c && c - 1 >= d) x += f[a][b][c - 1][d][e];
if (d && d - 1 >= e) x += f[a][b][c][d - 1][e];
if (e) x += f[a][b][c][d][e - 1];
}
cout << f[s[0]][s[1]][s[2]][s[3]][s[4]] << endl;
}
return 0;
}
详细解析:
矩阵
问题描述
把 1 ∼ 2020 放在 2 × 1010 的矩阵里。
要求同一行中右边的比左边大,同一列中下边的比上边的大。一共有多少种方案?
答案很大,你只需要给出方案数除以 2020 的余数即可。
答案提交
这是一道结果填空题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:1340
分析
要放当前数字并使其递增,只能放两个位置,就是第一行最右边的空格处和第二行最右边的空格处。
动态规划
f[i][j] 表示第一行有i个数,第二行有j个数的方式总数。
方程式:
f[ i ] [ j ] += f[i - 1][ j ] , f[ i ] [ j ] += f [ i ] [ j - 1 ] ,
表示第一行有i个数第二行有j个数,可以由两种方式得到:
第一种:当第一行有i-1个数,第二行有j个数时,在第一行添加一个数就可以得到f[i][j]。
第一种:当第一行有i个数,第二行有j-1个数时,在第二行添加一个数就可以得到f[i][j]。
#include <iostream>
using namespace std;
int dp[1020][1020];
int main()
{
dp[0][0] = 1; //设置初始值,一二行都不放数字时是一种方式。 // 两行一个数字都不放,也是一种方案
for (int i = 0; i <= 1010; i ++)
for (int j = 0; j<=1010&&j <= i; j ++)
{
//加if判断数组下表不为负数
if(i - 1 >= 0) // 转移前的状态也要合法,即第一行的数量不小于第二行的数量
dp[i][j] += dp[i - 1][j] % 2020;
if(j - 1 >= 0)
dp[i][j] += dp[i][j - 1] % 2020;
}
cout << dp[1010][1010] << endl;
return 0;
}