题目链接

https://www.dotcpp.com/oj/problem1837.html

题目大意

输入两个数,m,n分别代表A的数量和B的数量;
构建一个人塔,要求A脚底下的两个字母必须是A和A或者B和B,即脚底两个字母相同,B脚底下的两个字母必须是A和B或者B和A,即脚底两个字母不相同;
输出能排列出的种类数量。

解题思路

我的思路比较直白(傻),二进制枚举,枚举最底下一行的排列状态。最底下一行的排列状态确定后,上面的排列状态也就确定了,所以,只要统计构建的人塔中A和B的数量是否与输入相同,相同的话种类数+1。
什么是二进制枚举?以本题为例,每一个位置要么放A要么放B,这和二进制有点像啊,那就让A对应1,B对应0。
这样一来,1 1 0什么意思?A A B的意思吧。加入最底下一行有3个人,所有的可能,BBB,BBA,BAB,BAA,ABB,ABA,AAB,AAA,对应的二进制为,000,001,010,011,100,101,110,111,这不就是2^3吗。所以,最后一行的排列状态就可以通过循环表现出来了, for(int i=0;i<=(1<<3)-1;i++)

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=50;
int dp[N];//用一维数组表示二维状态,有点类似01背包问题优化的思想,但不是01背包!
int main(){
    int n,m,ans=0;
    cin>>m>>n;
    int h=(int)sqrt(2*(m+n));//计算出人塔的行数
    for(int num=0;num<=(1<<h)-1;num++){
        int tmp_h=h,tmp=num,cnt_a=0,cnt_b=0;
        for(int i=tmp_h;i>=1;i--,tmp>>=1){
            dp[i]=tmp&1;//初始化最底下一行
            if(dp[i]) cnt_a++;
            else cnt_b++;
        }

        while(tmp_h--){
            for(int i=1;i<=tmp_h;i++){
                if(dp[i+1] == dp[i]) cnt_a++,dp[i]=1;//set A
                else cnt_b++,dp[i]=0;//set B
            }
        }
        if(cnt_a==m && cnt_b==n) ans++;
    }
    cout<<ans<<endl;
}