题目描述
有 1~N 1 ~ N 共 N N 个数字,让你排成 排,要求第 i i 排有 个数字,同时满足每排上的数字单调递增,每列上的数字单调递增,求合法的方案数。 N≤30,k≤5 N ≤ 30 , k ≤ 5 。
吐槽
这个题书上的做法就是动态规划,想着很多状态用不到就写了个记忆化搜索,一边过样例,结果交上去 MLE
了,转眼看内存限制居然才
64MB
64
M
B
,搜网上的题解也基本上是用数学方法,但是我不会啊,所以灵机一动,用 malloc
函数开了个
5
5
<script type="math/tex" id="MathJax-Element-8">5</script> 维数组就过去了2333。
算法分析
一个显然的阶段是我们依次填入这些数字,这样保证了每排上的数字单调递增,然后我们只需限制转移时只有当前排上的数字个数比前一排少才能在这一排填入当前枚举的数字,就可以保证每列上的数字单调递增了,由于实际用到的状态数较少,采用记忆化搜索,动态开内存的方法就可以通过了。
代码实现
#include <cstdio>
#include <cstdlib>
typedef long long int ll;
int n,arr[5],cnt;ll***** f;
ll dp(int a,int b,int c,int d,int e) {
if(f[a][b][c][d][e]!=-1) return f[a][b][c][d][e];
ll ans=0;
if(a<arr[0]) ans+=dp(a+1,b,c,d,e);
if(a>b&&b<arr[1]) ans+=dp(a,b+1,c,d,e);
if(b>c&&c<arr[2]) ans+=dp(a,b,c+1,d,e);
if(c>d&&d<arr[3]) ans+=dp(a,b,c,d+1,e);
if(d>e&&e<arr[4]) ans+=dp(a,b,c,d,e+1);
return f[a][b][c][d][e]=ans;
}
ll***** ask(int a,int b,int c,int d,int e) {
ll***** p=(ll*****)malloc(sizeof(ll****)*a);
for(int i=0;i<a;++i) {
*(p+i)=(ll****)malloc(sizeof(ll***)*b);
for(int j=0;j<b;++j) {
*(*(p+i)+j)=(ll***)malloc(sizeof(ll**)*c);
for(int k=0;k<c;++k) {
*(*(*(p+i)+j)+k)=(ll**)malloc(sizeof(ll*)*d);
for(int l=0;l<d;++l) {
*(*(*(*(p+i)+j)+k)+l)=(ll*)malloc(sizeof(ll)*e);
}
}
}
}
for(int i=0;i<a;++i)
for(int j=0;j<b;++j)
for(int k=0;k<c;++k)
for(int l=0;l<d;++l)
for(int m=0;m<e;++m)
p[i][j][k][l][m]=-1;
return p;
}
int main() {
int n;
while(scanf("%d",&n)==1&&n) {
for(int i=0;i<5;++i) arr[i]=0;
for(int i=0;i<n;++i) scanf("%d",&arr[i]);
f=ask(arr[0]+1,arr[1]+1,arr[2]+1,arr[3]+1,arr[4]+1);
f[arr[0]][arr[1]][arr[2]][arr[3]][arr[4]]=1;
printf("%lld\n",dp(0,0,0,0,0));
}
return 0;
}