划分数、多重集组合数练习总结

#划分数练习总结
模板题poj1664
划分数描述的就是有N种相同的东西,将他们划分成M组,求有多少种不同的划分(1,2,5 和 1,5,2 是一样的),先来一段书上的话
这里写图片描述
其中那个错误推导看得懂是啥子意思,但是后面那个正确推导 :
dp[i][j] = dp[i-1][j] + dp[i][j-i]是啥子情况喃?
其中 dp[i-1][j]就代表 j个物品,在分成i-1组中一共有好多情况(其中就包括只分成1组、只分成2两组。。。), 那 dp[ i ] [ j - i ] 喃? 又表示啥子意思喃? 根据 dp[ i - 1] [ j] 的含义,我们现在要求的是 dp [ i ] [ j ] 也就是 j 个 物品分成 i 组有多少不同的情况, 我们现在已经晓得了分成 i - 1 组的所有情况,也就是说,只差必须分成 i 组的所有情况就得到答案了
dp[i][j-i]就代表把 j个物品必须分成 i组的情况,而要想 这i组,每组都分配的有东西,那起码每组应该有一个东西在里面占位置, 所有就是 从j个物品中拿出 i个物品先切这 i个分组中先占位, 所以就只剩了 j - i 个物品,然后这 j - i 个物品随便放,因为不管这 j - i 个物品怎么放, 肯定占 i 个分组的。而且这 j - i 个物品有多少种放法,前面已经动态规划出来了就是 dp[i][j-i]

#include<iostream>
#include<cstring>
using namespace std;
int length, row;
int shuzu[15][15];

int main(){
	int jishu;
	cin >> jishu;
	while(jishu --){
	    cin >> length >> row;
	    memset(shuzu, 0, sizeof(shuzu));
	    for(int i = 0; i <= length; i++) shuzu[1][i] = 1;
	    for(int i = 2; i <= row; i++){
		    for(int j = 0; j <= length; j++){
			    if(j < i) {
				    shuzu[i][j] = shuzu[i-1][j];   
				} else{
				    shuzu[i][j] = shuzu[i-1][j] + shuzu[i][j-i];
				} 
			}
		}
		
		cout << shuzu[row][length] << endl;
	}
    return 0;
}

#多重集组合数
模版题poj3046
说实话,不是炫耀,我刚刚学了划分数过后,还没有学多重集组合数的时候我就遇到了poj3046,我还是整出来了,虽然效率不咋样,但是还是AC了,还是可以。
多重集组合数划分数很像,划分数说的是有n完全一样的物品, 要将这些物品分装到 小于等于m 个不同的盒子中,有多少种不同的情况,而多重集组合数 说的是有n种不一样的物品,每个物品的数量若干,问将这些物品分装到m个盒子中有多少种不同的情况
还是先上书上讲的内容
这里写图片描述
这里写图片描述
我自己想的办法就是这里写图片描述
但是就跟书上说的一样,这个效率还是太低,容易超时,所以就要研究这里写图片描述
但是这个又咋个理解喃?
比如: 有3个物品,分别数量是 2 , 2 , 1, 然后将这些物品分成 3 组
根据dp [ i ] [ j ] 的含义,生成数组 :
1 , 1 , 0
2 , 3 , 2
3 , 5 , 5
其实还是不清楚,再来分别解释哈每个的含义,
我喜欢表示成 dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i][j-1-a]
其中dp[i-1][j] 求 i 行 j 列 的时候,上一行的同一列的元素,这个元素就代表还没有把第 i 个元素考虑进来的时候, 前 i - 1 个元素在放入 j 个盒子中有多少种情况。
dp[i-1][j-1] + dp[i-1][j-2] + ... + dp[i-1][j-k] 这些东西就代表在考虑第 i 种物品的情况时, 有分别考虑第 i 种物品只有 1 个时、 只有 2 个时、只有 3个时。。。只有min(i的数量, 最大盒子数) 时,但是这样一个一个加太浪费时间,而且我们之前已经把这些东西都加过一遍了,也就是 dp[i][j-1] 但是这个dp[ i ] [ j -1] 又有可能多加了一个东西,就是当 j-1-a >= 0 时,就会多加一个前面的dp[i-1][j-1-a] 就相当于平移造成的。 这相当于是在动态规划的基础上再利用动态规划

#include<iostream>
#include<cstring>
using namespace std;
#define N 1000000
int length, cishu, S, R;
int input[1005] = {0};
int shuzu[1002][100000] = {0};

int main(){
	cin >> length >> cishu >> S >> R;
	int aa;
	for(int i = 0; i < cishu; i++){
	    cin >> aa;
	    input[aa] ++;
	}
	
	for(int i = 0; i <= length; i++) shuzu[i][0] = 1;
	for(int i = 1; i <= length; i++){
	    for(int j = 1; j <= R; j++){
		    if(j-1-input[i] >= 0){
			    shuzu[i][j] = (N + shuzu[i-1][j] + shuzu[i][j-1] - shuzu[i-1][j-1-input[i]]) % N;
			}else{
			    shuzu[i][j] = (shuzu[i-1][j] + shuzu[i][j-1]) % N;
			}
		}
	}
	
	int answer = 0;
	for(int i = S; i <= R; i++) 
	answer = (answer + shuzu[length][i]) % N;
	cout << answer << endl;
    return 0;
}

没做出来的题poj1173
说实话这道题我方向都确定了,但是最后还是放弃了,看了答案过后还是有点遗憾。。。
还是转化成多重集组合数,但是给出一个排列求是第几个就不好整了,我的第一反应就是列举出所有的排列(从10111100 转化成 long long) 然后通过sort排序后,再用二分lower_bound() 方法来查找,但是感觉不好整,要是通过在多重集组合数dp的同时,记录所有的排列,情况太多了
这里写图片描述
肯定超时。
然后我又想要不然用背包dp来求,比如
输入 : 7 , 4 , 3
然后通过多重背包就可以求得有 1, 1, 2 , 3 和 1 , 2 , 2 , 2 这两种组合,然后用next_permutation() 来获得所有的排列,但是10个1,7个2,3个3就超时了
而且我想了个测试数据,输入33 , 19 , 33
这里写图片描述
这个答案太大了就说明就算能够得出所有的排列,也会超时因为
for(int i = 0 ; i < 471435600; i++) {} 就这一句话都要超时,就不要说其他的了,所以基本可以断定,这顶不是列举出所有排列,而是通过给出的排列直接得结果,但是又咋个整喃? 未必这些排列之间存在某种规律 ? 可以通过函数来表达排列?反正后头我就放弃了,直接看答案了。。。
其实,是通过上面多重集组合数动态规划的结果,来求的,比如说要求3121这个数是第几个,就是求3121前面有几个数,求3121前面有几个数,就是
先找第一位是1 和 2的所有排列
1 * * *
2 * * *
第一位是3的(也就是3 * * *)就不能现在直接照过来,因为要看后面几位
然后就是找第2位,因为这个排列原本是10101交替,所以奇数位就是数值越大就越大,但是偶数就是数值越大越小了,又因为前面把1 、2 开头的排列都找了,所以只剩3开头的3 3 * *
3 2 * *
然后3 1 * * 又不能慌到直接拿过来,就去找第三位,按照上面的规律
3 1 1 *
然后3 1 2 * 也是不能现在找出来
3 1 2 3
3 1 2 2
然后就完了。。。

关键就是咋个切在上面的多重组合数dp结果中找我们想要的东西
比如输入是 7 , 4 , 3,dp结果是
1 , 1 , 1 , 0 , 0 , 0 , 0
0 , 1 , 2 , 3 , 2 , 1 , 0
0 , 0 , 1 , 3 , 6 , 7 , 6
0 , 0 , 0 , 1 , 4 ,10 ,16

#include<iostream>
#include<cstring>
using namespace std;
int n, k, m;
int shuzu[35][35] = {0};
int jishu;
char input[40];

int getIndex(){
	int arr[40], length = 0;
	int qian = 1;
	for(int i = 1; input[i]; i++){
		if(input[i] != input[i-1]){
			arr[length++] = qian;
			qian = 0;
		}
		qian ++;
	}
	if(qian != 0) arr[length++] = qian;
	
	int answer = 0;
	int temp = n;
	for(int i = 0; i < length; i++){
		if(i%2 == 0){
			for(int j = 1; j < arr[i]; j++){
				if(temp > j)answer += shuzu[k-i-1][temp-j];
			}
		}else{
			for(int j = m; j > arr[i]; j--){
				if(temp > j)answer += shuzu[k-i-1][temp-j];
			}
		}
		temp -= arr[i];
	}	
	return answer;
}

int main(){
	cin >> n >> k >> m;
	shuzu[0][0] = 1;
	for(int i = 1; i <= k; i++){
		for(int j = 1; j <= n; j++){
			if(j-m-1 >= 0){
				shuzu[i][j] = shuzu[i-1][j-1] + shuzu[i][j-1] - shuzu[i-1][j-m-1];
			}else{
				shuzu[i][j] = shuzu[i-1][j-1] + shuzu[i][j-1];
			}
		}
	}	
	cout << shuzu[k][n] << endl;
	
	cin >> jishu;
	for(int i = 0; i < jishu; i++){
		cin >> input;
		int answer = getIndex();
		cout << answer << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Two_Punch/article/details/82501490