#划分数练习总结
模板题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;
}