题意
给定A和B两种机器人的个数M和N,A可以站在两个相同种类的机器人上面,B可以站在两个不同种类的机器人上面。要求站成一个三角形,问有多少种不同的方法。(N,M <500)
分析
已知条件
1)机器人总数 cnt = M+N,那么可以做多少层?可以这样思考,假设有num层,这就是长度为num步长为1的等差数列,那么塔内机器人总数cnt = num*( num+1)/2 = M+N,所以num = sqrt((M +N) *2)。
⒉)只要确定了底层机器人的摆放规则,就可以按照层数由低到高,依次算出每一排机器人的摆放。不同的机器人上面站A,相同的机器人上面站B。
解题思路
直觉的思路是,从最下一层,确定一个AB的排列(AB个数总数就是层数),推出上一层的排列…….直到第一层的机器人,在这个过程中记录使用到的A和B的数目,然后做一个check看这个由最下层决定的摆法是否合法,合法就答案+1;继续下一种最下层的AB排列。
那么为什么要先确定最底层排列呢?
因为每一层的机器人的排列取决于他下一层的机器人排列(最底层除外),所以只要得到最底层的机器人排列就能确定一个塔的排列(因为机器人个数有限制,所以不一定能够组成一个完整的塔)。
代码实现思路
我们来深入的思考一下题目,如果下方的两个机器人种类相同则上面应该站机器人B,如果下方的两个机器人种类不同则上方站机器人A,那么如果我们用0来表示机器人A,1来表示机器人B,我们来列一个表
0(右侧) | 1(右侧) | |
---|---|---|
0(左侧) | 0 | 1 |
1(右侧) | 1 | 0 |
从表中我们可以看出这个数据变换方式与异或相同,所以我们可以通过对下层的机器人的二进制表示进行二进制异或操作得到上一层的机器人排列
代码
#include<iostream>
#include<bitset>
#include<cmath>
using namespace std;
int n, m;
int nbit(int num) {
int ans = 0;
while(num){
num = (num-1) & num;
ans++;
}
return ans;
}
bool check(int now, int floor) {
int num_a = 0, num_b = 0;
for (int i = floor; i >= 1; i--) {//i是层数,也是机器人个数
int count1 = nbit(now);//now里面有多少1
num_b += count1;
num_a += i - count1;//now里面有多少0
//下面开始求上一层的now
now ^= now >> 1;
now &= (1 << (i - 1)) - 1; // 消掉高位
if (num_a > m || num_b >> n) return false;// 可以提升一点性能
}
return num_a == m && num_b == n;
}
int main() {
// freopen("/Users/zhengwei/CLionProjects/lq_final/2016_c_b/4/in6.txt", "r", stdin);
cin >> m >> n;
//利用double转int类型向下取整得性质,由(floor + 1) * (floor) / 2 == n + m 推导得
int floor = sqrt((n + m) * 2);//底行人数==层数
int ans = 0;
//2的floor次方种排列方案,每一种方案表示为i
for (int i = 0; i < (1 << floor); i++) {
if (check(i, floor))
ans++;
}
cout << ans << endl;
}