使用C++生成排列数(打印隔板法问题的所有解)

问题和思路来源于:https://www.zhihu.com/question/51448931

1. 问题描述

有n个相同的球,m个盒子(编号为1,2,……m),将这n个球放入这m个盒子中,要求输出所有可能的放置方法。

2. 问题思路

那这个正常情况下是用递归进行计算的,递归的话可能要在程序运行时开很大的内存(数据大的话)。
看到回答里有个老哥用位运算,没有调递归,我就试着写了一下状压版本的。
按照插板法进行模拟,一个m+n-1个位置,如果位置上是1的话,那么就是放板子的位置。如果是0的话,那么就是球的位置。这样把问题化成了一个指定总长度,指定1的个数的一个二进制数有哪些的问题了。二进制数有大小可以做参考,进行枚举的话不会漏掉的。
在枚举的过程中,将数字按从小到大进行排序枚举,每次在选择1的位置的时候采取贪心策略,也就是让尽量多的零在高位上。
当然,这次的代码也没评测姬,所以有bug的话,我会尽量的修复。

#include <iostream>
#include <bitset>
#include <algorithm>
#include <string>
using namespace std;
const int size = 1000;
bitset<size> tot;
int pos_h, pos_t;
int m=3, b=20;
int f[100][100];
void init() {
    for(int i = 0; i <= 99; i++) {
        f[i][i] = f[i][0]=1;
    }
    for(int i = 1; i <= 99; i++)
        for(int j = 1; j < i; j++)
            f[i][j] = f[i-1][j] + f[i-1][j-1];
}
void change(int bar) {
    int last = 0;
    int mid = 0;
    bool flag_mid= false;
    bool flag_begin = false;
    int cnt = 0;
    // cout << bar << " " << pos_h << endl;
    for (int i = 0; i < pos_h; i++) {
        if (tot[i]) flag_begin = true;
        if (tot[i] == 0) cnt++;
        if (tot[i] == 0 and flag_begin) {
            for (int j = 1; j <= cnt; j++) tot[i-j] = 0;
            for (int j = cnt+1; i-j >= 0; j++) tot[i-j] = 1;
            tot[i] = 1;
            return;
        }
    }
    tot.reset();
    for (int i = 0; i < bar-1; i++) tot.set(i);
    tot.set(pos_h);
    pos_h++;
}


int main() 
{
    init();
    ios::sync_with_stdio(false);
    cout << "input m b:";
    cin >> m >> b;
    freopen("out.txt", "w" ,stdout);	
    int bar = m - 1;
    pos_h = bar;
    int cnt = 0;
    for (int i = 0; i < bar; i++) tot.set(i);
    while (true) {
        // cout << tot << endl;
        string tmp = tot.to_string();
        reverse(tmp.begin(), tmp.end());
        // cout << tmp << endl;
        int last_pos = 0;
        for (int j = 0; j < m+b-1; j++) {
            if(tmp[j] == '1') {
                if (last_pos == 0 and tmp[0] == '0') last_pos--;
                cout << max(0, j - last_pos - 1) << " ";
                last_pos = j;
            }
        }
        cout << m+b-last_pos-2 << endl;
        cnt++;
        change(bar);
        if (tot[m+b-1]) break;
    }
    int ans = f[m+b-1][m-1];
    cout << "cal with comb is: " << ans <<endl;
    cout << "list length is: " << cnt << endl;
    return 0;
}
posted @ 2020-06-20 23:10  zprhhs  阅读(345)  评论(0编辑  收藏  举报
Power by awescnb