题目链接:https://www.acwing.com/problem/content/273/
题目要求将N个人排成不超过五列,每列的人数限制而且递减,现在要求每行每列都是递减的方案的数量,通过状态集合以及转移规律,f[a][b][c][d][e]满足索引递减的性质 ,在转移的时候要维护这个性质,所以除了e以为的所有的索引-1情况都需要考虑,e-1的情况自然维护了这个性质。此外,从高到低排这些人,当前要排的人排在哪一行就是决策的划分过程,当前决策中的方案数量等价于之前阶段的某一个决策的方案数,所以直接累加转移的方案即可。
在DP问题中,集合和集合划分的概念十分重要,本问题中的集合上的属性是数量。
代码:
#include<iostream> #include<cstdio> #include<string.h> using namespace std; const int maxn = 31; typedef long long ll; ll f[maxn][maxn][maxn][maxn][maxn]; int n; int main(){ while(scanf("%d",&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(s[1],a);b++) for(int c=0;c<=min(s[2],b);c++) for(int d=0;d<=min(c,s[3]);d++) for(int e=0;e<=min(s[4],d);e++) {// f[a][b][c][d][e]满足索引递减的性质 ll& v=f[a][b][c][d][e]; if(a && a>b)v+=f[a-1][b][c][d][e]; if(b && b>c)v+=f[a][b-1][c][d][e]; if(c && c>d)v+=f[a][b][c-1][d][e]; if(d && d>e)v+=f[a][b][c][d-1][e]; if(e)v+=f[a][b][c][d][e-1]; } printf("%lld ",f[s[0]][s[1]][s[2]][s[3]][s[4]]); } }