使用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]=1;
		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();
	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;
	}
	cout <<f[m+b-1][m-1] <<endl;
	cout << cnt << endl;
	return 0;
}


猜你喜欢

转载自www.cnblogs.com/cniwoq/p/13170849.html