题目描述
现在我们来考虑对一个有N层抽屉的柜子上锁。该柜子中的所有抽屉都整齐地排布在一列上,并且相邻上下两个抽屉之间没有木板分隔。也就是说,如果第一层的抽屉没有锁上,即使第二层的抽屉锁上了,我们也能够拿到第二层抽屉里面的东西(将第一层的抽屉抽出来)。我们的问题是,如果要求恰好只有M层抽屉里面的东西拿不到,总共有多少种不同的锁抽屉方式呢?
输入
输入包括两个数N和M,意义如题目中所描述。
输出
输出仅包含一个整数,即答案。
样例输入
6 4
样例输出
6
提示
对于20%的数据,有1≤N, M≤10;
对于50%的数据,有1≤N, M≤65;
对于100%的数据,有1≤N, M≤300。
解题思路:每个抽屉有锁和不锁两种状态,而且这层抽屉能否拿出只与自己和上一层抽屉的状态有关,所以想到dp。
dp[i][j][0]表示i层抽屉有j个拿不到并且这层抽屉是没锁的,dp[i][j][1]表示表示i层抽屉有j个拿不到并且这层抽屉是上锁的。
状态转移方程位(根据方程自己看一下很容易就理解了):
dp[i][j][1]=dp[i-1][j-1][1],dp[i-1][j][0];
dp[i][j][0]=dp[i-1][j][0],dp[i-1][j][1];
注意:在计数过程中,因数太大会导致爆炸,所以采用高精度
#include<bits/stdc++.h>
//#pragma GCC optimize(2)
#define N 100005
using namespace std;
typedef long long int ll;
typedef unsigned long long int ull;
const int mod=1e9+7;
const int INF=0x7fffffff;
const int inf=0x3f3f3f3f;
const ull base=131;
//高精加
string jia(string a,string b){
string s="";
int aa[1100],bb[1100],cc[1100];
memset(aa,0,sizeof(aa));
memset(bb,0,sizeof(bb));
memset(cc,0,sizeof(cc));
int lena=a.size(),lenb=b.size();
for(int i=1;i<=lena;i++) aa[i]=a[lena-i]-'0';
for(int i=1;i<=lenb;i++) bb[i]=b[lenb-i]-'0';
int len=1;
while(len<=lena||len<=lenb){
cc[len]+=aa[len]+bb[len];
if(cc[len]>=10){
cc[len+1]=cc[len]/10;
cc[len]%=10;
}
len++;
}
while(!cc[len]&&len>1) len--;
for(int i=len;i>=1;i--) s+=cc[i]+'0';
return s;
}
string dp[310][310][2];
int n,m;
int main(){
cin>>n>>m;
dp[0][0][1]=dp[1][0][0]=dp[1][1][1]="1";
for(int i=2;i<=n;i++) dp[i][0][0]=jia(dp[i-1][0][0],dp[i-1][0][1]),dp[i][0][1]=dp[i-1][0][0];//初始化边界
for(int i=1;i<=n;i++){
for(int j=1;j<=m&&i>=j;j++){
dp[i][j][1]=jia(dp[i-1][j-1][1],dp[i-1][j][0]);
dp[i][j][0]=jia(dp[i-1][j][0],dp[i-1][j][1]);
}
}
cout<<jia(dp[n][m][0],dp[n][m][1]);
return 0;
}