杨老师的照相排列

题目分析#

dp的难点在于状态的确定和转移方程的推导.

在本题中,我们可以从头枚举,去观察归纳,找到本题的最优子结构.

假设有k=1,a=[3,2,1].对于第一个数字1,它的数字一定是固定的——只能在最左上角;对于第二个数字,它可以位于1的右边,也可以在1的下边……

通过列举摆放的情况后,我们可以发现:

  1. i行的摆放数字的数量不超过ai

  2. 除了第一行之外,其他行摆放的数量都不超过上一行

为了保证题目中的要求,每个元素安放位置的枚举顺序也是按照从小到大开始的,那么我们可以让枚举过的元素组成一个集合,可以发现,元素放置的位置,即放置好的元素构成的形状,影响当前这个集合对应的方案数量

这样可以将一个子结构定义成f[a1][a2][a3][a4][a5],ak代表第k行摆放的元素的数量,对于摆放行数不够的情况,默认摆放成0

这样一来,我们就可以得到转移方程:在合法的情况下,f[]...[k + 1]... += f[]...[k]...

边界条件为f[0][0][0][0][0]=1

代码#

copy#include <iostream>
#include <cstring>
using namespace std;

typedef long long LL;

constexpr int N = 33;

LL f[N][N][N][N][N];
int n;
int s[N];

int main() {
  while (scanf("%d", &n), n) {
    memset(f, 0, sizeof f);
    memset(s, 0, sizeof s);
    for (int i = 1; i <= n; i++) scanf("%d", &s[i]);

    f[0][0][0][0][0] = 1;
    for (int a1 = 0; a1 <= s[1]; a1++)
      for (int a2 = 0; a2 <= min(a1, s[2]); a2++)
        for (int a3 = 0; a3 <= min(a2, s[3]); a3++)
          for (int a4 = 0; a4 <= min(a3, s[4]); a4++)
            for (int a5 = 0;a5 <= min(a4, s[5]); a5++) {
              LL &x = f[a1][a2][a3][a4][a5];
              if (a1 < s[1]) f[a1+1][a2][a3][a4][a5] += f[a1][a2][a3][a4][a5];
              if (a2 < s[2]) f[a1][a2+1][a3][a4][a5] += f[a1][a2][a3][a4][a5];
              if (a3 < s[3]) f[a1][a2][a3+1][a4][a5] += f[a1][a2][a3][a4][a5];
              if (a4 < s[4]) f[a1][a2][a3][a4+1][a5] += f[a1][a2][a3][a4][a5];
              if (a5 < s[5]) f[a1][a2][a3][a4][a5+1] += f[a1][a2][a3][a4][a5];
            }

    printf("%lld\n", f[s[1]][s[2]][s[3]][s[4]][s[5]]);
  }

  return 0;
}
posted @   Frank_Ou  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示
主题色彩