题目
分析
这是一个排列组合的题目,而排列组合最好是用位掩码表示。按照题目给出的测试数据,\(\binom{3}{5}\)应该按照升序输入如下10个组合(升序)以及对应的二进制掩码:
| 排列(升序) | 掩码 |
|---|---|
| 1 2 3 | 00111 |
| 1 2 4 | 01011 |
| 1 2 5 | 10011 |
| 1 3 4 | 01101 |
| 1 3 5 | 10101 |
| 1 4 5 | 11001 |
| 2 3 4 | 01110 |
| 2 3 5 | 10110 |
| 2 4 5 | 11010 |
| 3 4 5 | 11100 |
我们发现,排列的升序并不严格对应掩码的升序!比如,1 2 5应该先于1 3 4出现,但其对应的掩码却是01101 (1 3 4) < 10011 (1 2 5)!
再仔细观察,我们发现,如果按照降序排列,掩码可以严格保持降序:
| 排列(降序) | 掩码 |
|---|---|
| 5 4 3 | 11100 |
| 5 4 2 | 11010 |
| 5 3 2 | 10110 |
| 5 3 1 | 10101 |
| 5 2 1 | 10011 |
| 4 3 2 | 01110 |
| 4 3 1 | 01101 |
| 4 2 1 | 01011 |
| 3 2 1 | 00111 |
但这么一来,实际排列并不是我们需要的排列了(5 4 3 => 3 4 5,而我们需要1 2 3),但这是一个线性转换,容易写出转换的代码:
if (count == r) // exactly r numbers
{
for (int i = r - 1; i >= 0; i--)
{
cout << setw(3) << n - a[i];
}
cout << endl;
}
对于5, 4, 3,其对应的位是[4 3 2 ...],用n减去这个“位置”就正好是我们需要的1 2 3。
这就是本题的核心解法。
int total = (1 << n);
for (int S = total - 1; S >= 0; S--)
{
int count = 0;
for (int i = 0; i < n; i++)
{
if (S & (1 << i)) // Masked
{
a[count] = i;
count++;
}
}
if (count == r) // exactly r numbers
{
for (int i = r - 1; i >= 0; i--)
{
cout << setw(3) << n - a[i];
}
cout << endl;
}
}
答案

思考
本题虽然说不太难,但是要想清楚几点:
S的生成需要降序。- 掩码的设定需要由低到高,保证最高位置代表的数字出现在最后
- 遍历位置需要降序(和
S一致),并进行转换。
