抽屉上锁(计数dp+高精度)

题目描述
现在我们来考虑对一个有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;
}

上一篇:深入浅出Struts2+Spring+Hibernate框架


下一篇:kubernetes pod往宿主机拷贝文件