杨老师的照相排列
这道题想了很长时间,最后看题解才学会的,呜呜呜,我好菜呀!!
dp分析:
dp三要素:状态、阶段、决策。
- 状态: d p [ a ] [ b ] [ c ] [ d ] [ e ] dp[a][b][c][d][e] dp[a][b][c][d][e]从第 1 1 1排到第 5 5 5排放a~e人数的方案数。
- 状态划分:每一行存放的人数。
- 转移方程:如果前一行人数大于后一行人数:dp += dp(某一行人数-1);
- 边界: d p [ 0 ] [ 0 ] [ 0 ] [ 0 ] [ 0 ] = 1 dp[0][0][0][0][0] = 1 dp[0][0][0][0][0]=1
- 目标: d p [ a ] [ b ] [ c ] [ d ] [ e ] dp[a][b][c][d][e] dp[a][b][c][d][e]
题解:
- 我们从一个小的集合中向大的集合进行转移:如图所示,我们只能从当前的状态向左一个,或下一个方向进行转移,这里的限制条件就是前一行的人数要比下一行的人数大我们才能进行状态的转移。
- 我们遍历每一种状态来进行不同状态转移的加和
ACcode:
/*
* @Author: NEFU_马家沟老三
* @LastEditTime: 2020-10-01 20:46:41
* @CSDN blog: https://blog.csdn.net/acm_durante
* @E-mail: 1055323152@qq.com
* @ProbTitle:
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
ll dp[31][31][31][31][31];
int s[5];
int main()
{
int k;
while(~scanf("%d",&k) && k){
rep(i,1,k) scanf("%d",&s[i]);
rep(i,k+1,5) s[i] = 0;
mem(dp,0);
dp[0][0][0][0][0] = 1;
rep(a,0,s[1]) rep(b,0,min(s[2],a)) rep(c,0,min(s[3],b)) rep(d,0,min(s[4],c)) rep(e,0,min(s[5],d)){//这里min是指前一行比上一行的人数要多
if(a && a > b) dp[a][b][c][d][e] += dp[a - 1][b][c][d][e];//要想当前行转移的行的值不能为0,并且前一行要比上一行人数多才能进行填充人数
if(b && b > c) dp[a][b][c][d][e] += dp[a][b - 1][c][d][e];
if(c && c > d) dp[a][b][c][d][e] += dp[a][b][c - 1][d][e];
if(d && d > e) dp[a][b][c][d][e] += dp[a][b][c][d - 1][e];
if(e) dp[a][b][c][d][e] += dp[a][b][c][d][e - 1];
}
printf("%lld\n",dp[s[1]][s[2]][s[3]][s[4]][s[5]]);
}
return 0;
}