271. 杨老师的照相排列(填表法,线性dp)

阅读 95

2022-01-16

我们可以发现性质,

1.从左到右身高递减,从上到下身高也是递减,同时编号越大的同学身高越高

2.每排人数N1>=N2....

分析如下

 

 以此类推,其实我们可以发现一个规律

1.对于任意一排,我们都是从他最左边依次安排排列(不存在隔空安排)

2.对于任意一列,他当前被安排的长度都是>=后面所有列被安排长度

也就是说:行,列的安排,都必须是线性的,存在递推关系,前面安排了,后面才可以安排

我们就可以得到如下的规律

 任意一个排列的轮廓都是1,2这两种轮廓

所以我们可以得出,因为安排是线性的,有两种规律

1.必须同一行前面[1,i-1]都安排了,才能安排[i]

2.当前行已安排的数量不能大于前面任何一行的数量

有两种dp的分析方法

1.当前排列的子集合

根据当前排列可能的子集合进行划分,分析当前排列所有可能的来源

 2.当前排列的递推结果

根据当前排列递推后所有可能的结果作为我们的划分依据

两种思考方式最后得到的状态计算图都是一样的

状态计算对应集合的划分,令最后一个同学被安排在哪一排作为划分依据,可以将f[a][b][c][d][e]划分成5个不重不漏的子集:

  • 当a > 0 && a - 1 >= b时,最后一个同学可能被安排在第1排,方案数是f[a - 1][b][c][d][e];
  • 当b > 0 && b - 1 >= c时,最后一个同学可能被安排在第2排,方案数是f[a][b - 1][c][d][e];
  • 当c > 0 && c - 1 >= d时,最后一个同学可能被安排在第3排,方案数是f[a][b][c - 1][d][e];
  • 当d > 0 && d - 1 >= e时,最后一个同学可能被安排在第4排,方案数是f[a][b][c][d - 1][e];
  • 当e > 0时,最后一个同学可能被安排在第5排,方案数是f[a][b][c][d][e - 1]
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=31;
int kk[N];
typedef long long ll;
ll dp[N][N][N][N][N];

int main()
{
    int t;
    while(cin>>t,t)
    {
        int k;  //安排几列
        k=t;
        memset(kk,0,sizeof kk);
        memset(dp,0,sizeof dp);
        for(int i=0;i<k;i++)
            cin>>kk[i]; //每列人数
        //初始边界
        dp[0][0][0][0][0]=1;    //第一行0人...的方案数量是1
        for(int a=0;a<=kk[0];a++)
            for(int b=0;b<=min(a,kk[1]);b++) //不能大于前面的任何一行
                for(int c=0;c<=min(b,kk[2]);c++)
                    for(int d=0;d<=min(c,kk[3]);d++)
                        for(int e=0;e<=min(e,kk[4]);e++)//kk[4]==0,保证至少被执行一次
                        {
                            ll &x=dp[a][b][c][d][e];            
                            if(a&&a-1>=b)   //插入前a长度应该>=b
                                x+=dp[a-1][b][c][d][e];
                            if(b&&b-1>=c)
                                x+=dp[a][b-1][c][d][e];
                            if(c&&c-1>=d)
                                x+=dp[a][b][c-1][d][e];
                            if(d&&d-1>=e)
                                x+=dp[a][b][c][d-1][e];
                            if(e)
                                x+=dp[a][b][c][d][e-1];
                        }
        cout<<dp[kk[0]][kk[1]][kk[2]][kk[3]][kk[4]]<<'\n';
    }
    return 0;
}

精彩评论(0)

0 0 举报