蓝桥杯2114 李白打酒加强版
问题描述
话说大诗人李白, 一生好饮。幸好他从不开车。
一天, 他提着酒显, 从家里出来, 酒显中有酒 2 斗。他边走边唱:
无事街上走,提显去打酒。 逢店加一倍, 遇花喝一斗。
这一路上, 他一共遇到店 N 次, 遇到花 M 次。已知最后一次遇到的是花, 他正好把酒喝光了。
请你计算李白这一路遇到店和花的顺序, 有多少种不同的可能?
注意: 显里没酒 ( 0 斗) 时遇店是合法的, 加倍后还是没酒; 但是没酒时遇 花是不合法的。
输入格式
第一行包含两个整数 N 和 M.
输出格式
输出一个整数表示答案。由于答案可能很大,输出模 1000000007 的结果.
样例输入
5 10
样例输出
14
样例说明
如果我们用 0 代表遇到花,1 代表遇到店,14 种顺序如下:
010101101000000
010110010010000
011000110010000
100010110010000
011001000110000
100011000110000
100100010110000
010110100000100
011001001000100
100011001000100
100100011000100
011010000010100
100100100010100
101000001010100
评测用例规模与约定
对于 40% 的评测用例: 1≤N,M≤10 。
对于 100%的评测用例: 1≤N,M≤100 。
难。。
#include<iostream>
using namespace std;#define int long long
const int mod = 1000000007;
const int N = 110;int n, m;//dp[i][j][k]:遇到i次店,遇到j次花,剩余酒量为k时的顺序数量
int dp[N][N][N]; signed main()
{cin>>n>>m;dp[0][0][2] = 1; //遇到店的数量for(int i=0; i<=n; ++i) {//遇到的花的次数for(int j=0; j<=m-1; ++j)//因为最后一次是花,所以之前最多遇到 m-1 次花{//遍历当前剩余的酒量//因为每次遇花喝一斗,最多喝 m 斗,所以酒量不会超过 mfor(int k=0; k<=m; ++k){//遇到花 if(j>=1 && k>=1) //j>=1:j-1>=0, k>=1:当前酒量至少为1斗{//逆向逻辑:花的总次数从j-1增加到j,酒量从k+1减少到kdp[i][j][k] = dp[i][j-1][k+1]; //单纯的赋值,数值不会增长,因此不需要取模}//遇到店 if(i>=1 && k%2==0){//逆向逻辑:店的总次数从i-1增加到i,酒量从k/2加倍到kdp[i][j][k] = (dp[i][j][k] + dp[i-1][j][k/2]) % mod; //加法操作,数值会增长,必须取模}}}}//遇到 n 次店,m - 1 次花,酒量为1斗的方案数cout<<dp[n][m-1][1];return 0;
}