AcWing 271杨老师的照相排列

思路

\(1=<k<= 5\),所以最多会有五个位置,五个位置分配人,即集合为f[a][b][c][d][e]表示五个位置各放了a, b, c, d, e个人的方法的数量,同时h[1]>=h[2]>=h[3]>=h[4]>=h[5],所以后面的层也可以取到和上面一层一样的人数,但是不能超过,因此b<= min(a, s[2])
我们考虑最后一个同学的位置,其实就是五个位置都考虑一下,因此我们需要满足这一层人数减去1也大于等于下一层,那样就可以放在当前位置,同时该层得为正数,不然例如a-1如果小于0就要段错误了

ps: 数据还挺极限的,这f开到f[35][35][35][35][35]就寄了,如果是是32就不会超时,应该是memset上花了很多时间

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()
#define SZ(x) (int) (x).size()
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef vector<int> VI;
typedef double db;

const int N = 32;
int n, s[10];
ll f[N][N][N][N][N];
void solve() {
  memset(s, 0, sizeof(s));
  for (int i = 1; i <= n; i++) cin >> s[i];
  memset(f, 0, sizeof(f));
  f[0][0][0][0][0] = 1; // 哪里都不放人只有一种选择
  for (int a = 0; a <= s[1]; a++) {
    for (int b = 0; b <= min(a, s[2]); b++) {
      for (int c = 0; c <= min(b, s[3]); c++) {
        for (int d = 0; d <= min(c, s[4]); d++) {
          for (int e = 0; e <= min(d, s[5]); e++) {
            ll &x = f[a][b][c][d][e];
            if (a && a-1 >= b) x += f[a-1][b][c][d][e];
            if (b && b-1 >= c) x += f[a][b-1][c][d][e];
            if (c && c-1 >= d) x += f[a][b][c-1][d][e];
            if (d && d-1 >= e) x += f[a][b][c][d-1][e];
            if (e) x += f[a][b][c][d][e-1];
          }
        }
      }
    }
  }
  cout << f[s[1]][s[2]][s[3]][s[4]][s[5]] << '\n';
} 

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  // int tt;
  // cin >> tt;
  while (cin >> n, n) {
    solve();
  }
  return 0;
}

posted on 2022-10-26 00:02  chelly酱  阅读(6)  评论(0)    收藏  举报

导航