poj 2279 dp(防MLE) 或 数论(杨氏矩阵+勾长公式)

9 篇文章 0 订阅

题目链接:poj 2279

这个题目 有两种做法 dp 或者是 杨氏矩阵+勾长公式

首先是dp做法(参考lyd的书)  这有个小技巧 就是 31的5次方会爆内存  所以我们得开个可变的数组  就是 先把那5维的大小输入 在创建数组  这样可以节省很多空间  另外像这种可变数组是不能全局声明的

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int N[5];
int main(){
	int k;
	while(scanf("%d",&k)&&k){
		memset(N,0,sizeof(N));
		for(int i = 0; i < k; i++) scanf("%d",&N[i]);
		ll dp[N[0]+3][N[1]+3][N[2]+3][N[3]+3][N[4]+3];
		memset(dp,0,sizeof(dp));
		dp[0][0][0][0][0]=1;
		for(int i = 0; i <= N[0]; i++){
			for(int j = 0; j <= N[1]; j++){
				for(int x = 0; x <= N[2]; x++){
					for(int y = 0; y <= N[3]; y++){
						for(int p = 0; p <= N[4]; p++){
							ll num = dp[i][j][x][y][p];
							if(i<N[0])dp[i+1][j][x][y][p]+=num;
							if(j<N[1]&&j<i) dp[i][j+1][x][y][p]+=num;
							if(x<N[2]&&x<j) dp[i][j][x+1][y][p]+=num;
							if(y<N[3]&&y<x) dp[i][j][x][y+1][p]+=num;
							if(p<N[4]&&p<y) dp[i][j][x][y][p+1]+=num;
						}
					}
				}
			}
		}
		printf("%lld\n",dp[N[0]][N[1]][N[2]][N[3]][N[4]]);
	}
	return 0;
}

杨氏矩阵+勾子公式  看题目的样例 我们可以发现它是一个倒过来的杨氏矩阵  

杨氏矩阵是啥咧

例如  

4 5 6

1 2 3 4

这样的  每一行从左往右递增  每一列 从前往后递增的  就叫杨氏矩阵(不一定是个矩阵)

它是有计算公式的  ,  在那之前还需要一个定义  就是勾子长度:对于一个格子来说 勾子长度是其右边的格子数目+其上方的各自数目+1(自己)  

然后由勾长公式可以得到 满足这样的形状的杨式矩阵个数   tot = n!/(\prod_{i=1}^{n} \varphi (i)) 其中n是格子数目  \varphi (i)是i这个格子的勾子长度 那我们算一下每个 格子的勾子长度 在用公式求一下这题就行了   注意溢出的问题  

#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
ll n,cnt,x,y,tmp,num[40],sum[50];
int main()
{
    while(scanf("%lld",&n)&&n){
        memset(sum,0,sizeof(sum));
        tot=0,x=1,y=1;
        for(int i=1;i<=n;i++)scanf("%lld",&num[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=num[i];j++){
            	++tot;
                for(int k=i+1;k<=n;k++){
                    if(num[k]>=j)sum[cnt]++; //向上找格子数 
                    else break;
                }
                sum[tot]+=num[i]-j+1; //右边包括自己的格子数 
            }
        for(int i=1;i<=tot;i++){
            x*=i;y*=sum[i];
            tmp=__gcd(x,y);
            x/=tmp;y/=tmp;//除gcd防止溢出 
        }
        printf("%lld\n",x/y);
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值