题目描述
标号为 1 ~ N 的同学站成 k 行,每行人数为
a
i
a_i
ai,规则入下图所示,同一行标号从左向右递增,同一列标号从后向前递减,前面的列数不长于后面的列数,问方案有多少种。
题解:
- 满足两个方向的递增:因为在合法的方案中,每行每列都是递增的,所以我们可以考虑从标号 1 到 N 开始放,对于同一行的递增,后放的一定大,满足了从左到右递增;对于同一列的递增,后放的也一定大(要满足这一列的行是最后一行或者要放的这一行同一列后面的行已经放了数),这满足了从后向前递增。
- 满足前面行数不大于后面行数,所以要满足两个条件:在放的时候(1)考虑行:令 n o w i now_i nowi 表示当前第 i 行放的个数,要满足 n o w i < a i now_i<a_i nowi<ai;(2)考虑列:这一列是最后一列 i = 1 i = 1 i=1 或者 n o w i − 1 > n o w i now_{i - 1} > now_i nowi−1>nowi 。
- 状态转移方程详细见代码。
AC Codes:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 31, M = 1e9 + 7, INF = 0x3f3f3f3f;
unsigned int dp[N][N][N][N][N];
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int k;
while (cin >> k && k) {
vector<int> a(6, 0);
for (int i = 1; i <= k; i++) cin >> a[i];
for (int i = 0; i <= a[1]; i++)
for (int j = 0; j <= a[2]; j++)
for (int k = 0; k <= a[3]; k++)
for (int x = 0; x <= a[4]; x++)
for (int y = 0; y <= a[5]; y++)
dp[i][j][k][x][y] = 0;
dp[0][0][0][0][0] = 1;
for (int i = 0; i <= a[1]; i++)
for (int j = 0; j <= a[2]; j++)
for (int k = 0; k <= a[3]; k++)
for (int x = 0; x <= a[4]; x++)
for (int y = 0; y <= a[5]; y++) {
if (i < a[1]) dp[i + 1][j][k][x][y] += dp[i][j][k][x][y];
if (j < a[2] && j < i) dp[i][j + 1][k][x][y] += dp[i][j][k][x][y];
if (k < a[3] && k < j) dp[i][j][k + 1][x][y] += dp[i][j][k][x][y];
if (x < a[4] && x < k) dp[i][j][k][x + 1][y] += dp[i][j][k][x][y];
if (y < a[5] && y < x) dp[i][j][k][x][y + 1] += dp[i][j][k][x][y];
}
cout << dp[a[1]][a[2]][a[3]][a[4]][a[5]] << '\n';
}
return 0;
}