蓝桥杯 C++ b组李白打酒加强版,动规及dfs+记忆化搜索双解
题目大意:初始有2斗酒,遇到花会喝1斗,遇到店使当前酒翻倍,最后遇到的是花,且最好酒为0;并且不会出现酒<0的情况,给定经过了n次店,m次花,求总共有多少种遇到花和店的顺序、
解法1:动态规划
(1)状态数组:f[i][j][k]:已经经过了i个店和j个花,并且当前酒为k斗,其值表示为方案数
(2)状态转化公式:f[i][j][k]可以经过了一个店得到,也可以经过一个花得到
a.店:到上一个店的方案数为f[i-1][j][k/2]:表示已经走了i-1个店,j个花,当前酒为k/2;
ps:i>=1,k%2==0
b.花:到上一个花的方案数为f[i][j-1][k+1]:已经走了i个店,j-1个花,当前酒为k+1
ps:j>=1
(3)初始化:初始经过0个店和花,且酒为2,故应初始化f[0][0][2],并且不论经过一个花还是一个店,对于可能的下一项f[1][0][4]或者f[0][1][1],它俩的方案数肯定都是1,因为只能从f[0][0][2]得到,再结合我们的转化公式(详见代码吧,更好理解),可知初始化f[0][0][2]=1;
ps:下面的f[i][j][k],我们可以在使用前定义int& v=f[i][j][k],然后在本来使用f[i][j][k],替换为v了,可以去搜一下相关使用~~QWQ
参考代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 105,MOD=1e9+7;
using ll=long long;
int n,m;
int f[N][N][N];
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
f[0][0][2]=1;//经过0个花和店,手里2斗酒,即初始状态;
for(int i=0;i<=n;i++)//店
{
for(int j=0;j<=m;j++)//花
{
for(int k=0;k<=m;k++)//不会超过花的次数
{
if(i>=1 && k%2==0) f[i][j][k]=(f[i][j][k]+f[i-1][j][k/2])%MOD;
if(j>=1) f[i][j][k]=(f[i][j][k]+f[i][j-1][k+1])%MOD;
}
}
}
cout<<f[n][m-1][1];
return 0;
}
解法2:dfs+记忆化搜索
(1)首先要考虑的是如何定义dfs,传的参数是什么?显然是店,花,酒,又因为咱们这里用正向搜索,这里就定义dfs(剩余的店,剩余的花,现在的酒)
(2)其次开一个三维数组,d[n][m][k]表示有n店,m花,此刻酒为k时到题中最后的情况(遇到花喝完酒)总共的方案数。一开始全部初始化为-1,即memset(d,-1,sizeof d);
(3)然后我们要明确dfs的返回值,这里先给公式
d[n][m][k]=(dfs(n-1,m,k*2)+dfs(n,m-1,k-1))%MOD;
余n店,m花的情况走一步只会得到余n-1店,m花或余n店,m-1花的情况
那么对于d[n][m][k]的值当然就是两种方案数相加,但k的变化需要注意一下,这里和动态规 划的k刚好相反,这里是由初始状态向后面走,所以时2*k和k-1。
(4)最后就到了记忆化了,首先已经搜过的可以不用重复搜,即d已经赋值了的,d!=-1时,直接返回当前d[][][];其次还可以特判一下非法的情况,比如,花和店以及酒到最后才是负数,不可能为0;另外,酒的数量一定不会超过100,因为花的最大值为100,而每个花只能让酒-1;最后如果花为0了,而此时店和酒还不为0,那么也不合法!因为店只会让酒增加,无论如何酒最后都不会为0;
最后就是代码了:
#include<bits/stdc++.h>
using namespace std;
const int N = 105,MOD=1e9+7;
using ll=long long;
int n,m;
int d[N][N][N];
int dfs(int n,int m,int k)//还余下n个店,m个花,现在酒为k
{
if(n<0||m<0||k<0||k>=N||(!m&&(n||k))) return 0;
if(~d[n][m][k]) return d[n][m][k];
return d[n][m][k]=(dfs(n-1,m,k*2)+dfs(n,m-1,k-1))%MOD;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
memset(d,-1,sizeof d);
d[0][1][1]=1;//最后的情况,剩下1个花,1斗酒
cout<<dfs(n,m,2);
return 0;
}