1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x51線性DP 照相館排列

《演算法競賽進階指南》0x51線性DP 照相館排列

題目連結: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\n",f[s[0]][s[1]][s[2]][s[3]][s[4]]); } }