《演算法競賽進階指南》0x51線性DP(4/7)
阿新 • • 發佈:2021-10-19
A-Mr. Young's Picture Permutations
題意:有\(n\)位同學現在想把他們分成\(k\)橫排,每橫排的人數為\(a_1,a_2,\cdots,a_k\),分配時應該遵循的規則是,每一橫排的同學身高應該從左到右是遞減的,每一豎排的同學身高應該前到後遞減的,問一共有幾種的分配方法,\(\sum_{i=1}^{i=k}a_i \le 30,k \le 5\)。
思路:
同學的身高不需要具體知道,直接用\(1-n\)表示身高從小到大即可
用一個多維的狀態來表示排第\(i\)名學生時,\(k\)排已經排好的\(i-1\)名學生對應的狀態,考慮對於第\(x\)排在這種狀態下滿足何種條件可以在放置一個新同學。
\(1.\)
\(2.\)當前第\(x\)排的人數應當小於\(x-1\)排的人數,否則後面放置的身高一定更高,就沒有合法的放置方案了。
#include <bits/stdc++.h> using namespace std; #define pb push_back #define eb emplace_back #define MP make_pair #define pii pair<int,int> #define pll pair<ll,ll> #define lson rt<<1 #define rson rt<<1|1 #define CLOSE std::ios::sync_with_stdio(false) #define sz(x) (int)(x).size() typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-6; const int N = 35; int n,a[N]; ll f[N][N][N][N][N]; void solve() { int sum = 0; for(int i = 1;i <= n;i ++) { scanf("%d",&a[i]); sum += a[i]; } f[0][0][0][0][0] = 1; for(int i = 0;i <= a[1];i ++) { for(int j = 0;j <= a[2];j ++) { for(int k = 0;k <= a[3];k ++) { for(int t = 0;t <= a[4];t ++) { for(int w = 0;w <= a[5];w ++) { if(i) { f[i][j][k][t][w] += f[i-1][j][k][t][w]; } if(j && j <= i) { f[i][j][k][t][w] += f[i][j-1][k][t][w]; } if(k && k <= j) { f[i][j][k][t][w] += f[i][j][k-1][t][w]; } if(t && t <= k) { f[i][j][k][t][w] += f[i][j][k][t-1][w]; } if(w && w <= t) { f[i][j][k][t][w] += f[i][j][k][t][w-1]; } } } } } } printf("%lld\n",f[a[1]][a[2]][a[3]][a[4]][a[5]]); // memset(f,0,sizeof(f)); for(int i = 0;i <= a[1];i ++) { for(int j = 0;j <= a[2];j ++) { for(int k = 0;k <= a[3];k ++) { for(int t = 0;t <= a[4];t ++) { for(int w = 0;w <= a[5];w ++) { f[i][j][k][t][w] = 0; } } } } } for(int i = 1;i <= n;i ++) a[i] = 0; } int main() { while(~scanf("%d",&n)) { if(n == 0) break; solve(); } return 0; }