题目:http://poj.org/problem?id=2279
有dp做法,但会MLE。
dp的思想很好,是通过 “按身高由小到大往进放” 把 “身高小于” 的条件转化成 “放进去的先后” ,于是又变成 “当前位置的上边和左边是否已放” 。
“左边” 通过对每一行长度的记录;“上边” 通过与上一行长度的比较,就可以状态转移了。
正解是用公式。杨氏矩阵(虽然没用上)+钩子公式。
dp代码:
#include<iostream>
#include<cstdio>
using namespace std;
int n,k,lm[];
unsigned long long d[][][][][];
int main()
{
scanf("%d%d",&n,&k);
for(int i=;i<=k;i++)
scanf("%d",&lm[i]);
d[][][][][]=;
for(int i=;i<=lm[];i++)
for(int j=;j<=lm[]&&j<=i&&i+j<=n;j++)
for(int r=;r<=lm[]&&r<=j&&i+j+r<=n;r++)
for(int l=;l<=lm[]&&l<=r&&i+j+r+l<=n;l++)
for(int k=;k<=lm[]&&k<=l&&i+j+r+l+k<=n;k++)
{
d[i][j][r][l][k]+=d[i-][j][r][l][k];
if(j>)d[i][j][r][l][k]+=d[i][j-][r][l][k];
if(r>)d[i][j][r][l][k]+=d[i][j][r-][l][k];
if(l>)d[i][j][r][l][k]+=d[i][j][r][l-][k];
if(k>)d[i][j][r][l][k]+=d[i][j][r][l][k-];
}
printf("%lld",d[lm[]][lm[]][lm[]][lm[]][lm[]]);
return ;
}
公式代码:
#include<iostream>
#include<cstdio>
#define ull unsigned long long
using namespace std;
ull n,k,r,c[],a[][],cnt;
ull gcd(ull a,ull b)
{
return b==?a:gcd(b,a%b);
}
int main()
{
while()
{
scanf("%lld",&n);
if(!n)return ;
for(int i=;i<=n;i++)
{
scanf("%lld",&c[i]);
for(int j=;j<=c[i];j++)
a[i][j]=c[i]-j;
}
k=;r=;cnt=;
for(int i=n;i;i--)
{
for(int j=;j<=c[i];j++)
{
for(int l=i;l;l--)
a[l][j]++;
ull gd=gcd(k*(++cnt),r*a[i][j]);
k=k*(cnt)/gd;
r=r*a[i][j]/gd;
// printf("i=%d j=%d a=%lld k=%lld r=%lld\n",i,j,a[i][j],k,r);
}
}
printf("%lld\n",k/r);
}
}