poj2279 Mr. Young's Picture Permutations[勾长公式 or 线性DP]

若干人左对齐站成最多5行,给定每行站多少个,列数从第一排开始往后递减。要求身高从每排从左到右递增(我将题意篡改了便于理解233),每列从前向后递增。每个人身高为1...n(n<=30)中的一个数(互不不同)。求可行方案数。(地址点我qwq);


做了lyd书dp这一章的第一题,就不会qwq。。果然菜的人还是永远菜啊,注定翻不了身。

lyd的书上讲到了dp的方法,不是很理解。后来想通了,发现自己想的时候也想到了这一点转化,但是又很快把他抛弃掉了。。冏。

上面所谓的转化就是说,我本来安排人去排列,是无序的,显然也不好dp,因为高度不定,选了哪些也不知道,对后续状态有影响,所以要转化为有序的dp。有没有觉得很熟悉?BZOJ1079 [SCOI2008]着色方案[组合计数DP]这里就是将无序的排列改为有序的插入。

对于要求从前面的一些状态中有条件限制的转移,当条件不易表达时,尝试将无序选择转化为有序的顺次选择简化之。

为什么顺序插入身高是对的?假设我已经插入$i$个身高最矮的人,那么身高$i+1$的人除了插入每一行的末尾别无选择,试想如果插在排好的队中,显然不单调。和末尾空了一格或多格站,那么中间就找不到合适身高的人站进去了。当然也要保证插入的行的当前人数少于上一行的,不然上一行会比这一行少空位,之后身高更高的人也没办法站上去。这样做完,就可以保证排列方案满足要求。有没有什么方案不能通过上面这种方案构造出来的?没有,因为不按上面那种插入的话,其他情况必然无解。所以对于每一种可行方案,我可以认为他是按从小到大身高顺序每个依次在某一行末尾插入的得到的。那么我就可以顺理成章设f[a][b][c][d][e]来记录状态了,然后就是lyd书上的做法。对于每个状态,去推出其他可以拓展的后续状态(在这题也就是再插入身高排名下一位的人)。

实现上dp数组开满会爆内存,可以采用动态开数组(说白了就是主函数内部根据大小开数组),或者f[31][16][11][8][7]也可以,为什么您们都看得出来。。然而前者速度吊打后者,因为是动态开的嘛,后者每次还要全部清空。

其他一些细节(比如不满5行怎么办)看code,也可以照例解决,就把空的几行看成数组下标是0即可。

另:勾长公式挂在标题上是摆设。。我不太想学,因为有点冷门,而且应当不会考这玩意儿的说。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 8 #define ddbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
 9 using namespace std;
10 typedef long long ll;
11 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
12 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
15 template<typename T>inline T read(T&x){
16     x=0;int p=0;char c;while(!isdigit(c=getchar()))if(c=='-')p=1;
17     while(isdigit(c))x=x*10+(c&15),c=getchar();return p?x=-x:x;
18 }
19 int x[6];
20 int n;
21 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
22     while(read(n),n){
23         memset(x,0,sizeof x);
24         for(register int i=1;i<=n;++i)read(x[i]);
25         unsigned int f[x[1]+2][x[2]+2][x[3]+2][x[4]+2][x[5]+2];
26         memset(f,0,sizeof f);
27         f[0][0][0][0][0]=1;
28         for(register int a=0;a<=x[1];++a)
29             for(register int b=0;b<=x[2];++b)
30                 for(register int c=0;c<=x[3];++c)
31                     for(register int d=0;d<=x[4];++d)
32                         for(register int e=0;e<=x[5];++e){
33                             f[a+1][b][c][d][e]+=f[a][b][c][d][e];
34                             if(b<a)f[a][b+1][c][d][e]+=f[a][b][c][d][e];
35                             if(c<b)f[a][b][c+1][d][e]+=f[a][b][c][d][e];
36                             if(d<c)f[a][b][c][d+1][e]+=f[a][b][c][d][e];
37                             if(e<d)f[a][b][c][d][e+1]+=f[a][b][c][d][e];
38                         }
39         printf("%u
",f[x[1]][x[2]][x[3]][x[4]][x[5]]);
40     }
41     return 0;
42 }
原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10656443.html