CF295C Greg and Friends(线性DP)

题目

m个50kg的和n个100kg的一群人要过河,有且仅有一条可载重量为k的船,请问最少多少次渡河全部渡过去。

思路

值得深思的一道线性DP题目
  通过杨辉三角形可以快速求出组合数,进一步则有A[a][b][c][d]=从a个人中选择c个人,同时在b个人中选择d个人可能的方案。确定状态dp[i][j][k] 表示第i个行程,起点处有j个50kg的人,k个100kg的人。
  状态转移方程运过去的转移方程
  dp[i][x-a][y-b]+=A[x][y][a][b]*dp[i-1][x][y])%=Mod; 减少a个50,b个100
  运回来
  (dp[i+1][x+a][y+b]+=A[X-x][Y-y][a][b]*dp[i][x][y])%=Mod;增加a个50,b个100

#include<bits/stdc++.h>
#define Mod 1000000007
const int M=55;
long long dp[M*4][M][M],C[M][M],A[M][M][M][M];
int main(){
	for(int i=0;i<M;++i) for(int j=C[i][0]=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
	for(int a=0;a<M;++a) for(int b=0;b<M;++b) for(int c=0;c<=a;++c) for(int d=0;d<=b;++d) A[a][b][c][d]=C[a][c]*C[b][d]%Mod;
	int n,k,X=0,Y=0;
	scanf("%d %d",&n,&k);
	for(int i=0,a;i<n;++i) scanf("%d",&a),++(a==50?X:Y);
	dp[0][X][Y]=1;
	for(int i=1;i<=4*n+1;i+=2){
		long long res=0;
        //运过来
		for(int x=0;x<=X;++x) for(int y=0;y<=Y;++y) if(dp[i-1][x][y]){
			for(int a=0;a<=x;++a) for(int b=0;b<=y;++b) if((a|b) && a*50+b*100<=k){
				(dp[i][x-a][y-b]+=A[x][y][a][b]*dp[i-1][x][y])%=Mod;
			}
		}
		if(dp[i][0][0]) return printf("%d\n%lld\n",i,dp[i][0][0]),0;
		//运回去
		for(int x=0;x<=X;++x) for(int y=0;y<=Y;++y) if(dp[i][x][y]){
			for(int a=0;a<=X-x;++a) for(int b=0;b<=Y-y;++b) if((a|b) && a*50+b*100<=k){
				(dp[i+1][x+a][y+b]+=A[X-x][Y-y][a][b]*dp[i][x][y])%=Mod;
			}
		}
	}
	puts("-1\n0");
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值