复杂的整数划分问题

来源:互联网 发布:matlab 遗传算法 编辑:程序博客网 时间:2024/05/22 09:46

8:复杂的整数划分问题

  • 查看
  • 提交
  • 统计
  • 提问
总时间限制: 
200ms 
内存限制: 
65536kB
描述

将正整数表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 k>=1 
正整数的这种表示称为正整数的划分。

输入
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。 
(0 < N <= 50, 0 < K <= N)
输出
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
样例输入
5 2
样例输出
233
提示
第一行: 4+1, 3+2,
第二行: 5,4+1,3+2

第三行: 5,1+1+3, 1+1+1+1+1+1


首先一个坑爹的地方是我TM以为输入只有一行。。。然后就直接cin>>n>>k。。。。mdzz,搞了我一小时,查不出错lol。


下面说说思路吧,其实n拆成k个数是很好想状态方程的,dp[n][k]代表把n拆成k个数的方法。为了让问题规模变小,开始考虑将n拆成k个数有哪些情况,可以分为第一个数是1还是大于1的情况,如果不是1,那这些方法的集合的总数就是dp[n-k][k],也就是相当于每个数都减一,这样能够保证减完之后不会出现0,不禁想到了上学期的放苹果问题。还有一种情况是第一个数是1,就是dp[n-1][k-1],所以状态转移方程就是把这两项相加即可,并不是很难。


然后第二问,拆成不同正整数的方法个数,这个玩意我就根本想不到怎么定义状态,中午农园电梯上问了一下TK,他说还是和第一问差不多的,dp[n][k]代表把n分成k个不同的数的方法。。。看来果然和高中考试一样,前几问往往会是后面的问题的铺垫lol。还是一样,分成第一个是不是1讨论,如果不是1,那么就是dp[n-k][k],如果是1,一开始我TMzz地写成dp[n-1][k-1],然后发现TM怎么和第一个问题一模一样。。。然后突然发现了问题,扔掉第一个1之后剩余部分并不是一个相同内容的子问题,因为剩余部分隐含着要求第一个数(也就是原来第二个数)大于1,但事实上子问题对于第一个数字是完全没有限定的。。。这TM就尴尬了,同时也就意味着不能这么分解子问题。那么还能怎么分解捏。。。然后想,如果每个数都减一,那么第一个1肯定就没有了,然后变成了dp[n-k][k-1],为什么可以这样呢。。。先惯例把1扔掉,然后剩下的部分就变成了把n-1分解成k-1个不同的数,同时第1个数是不小于2开始的,但是我们的要求是第一个数不小于1的,那如果把每个数都减一,那么就能保证分解是从1开始的,注意这时候总数就变成了n-1-(k-1)了,最后整理得到dp[n-k][k-1].


第三问其实原理和第二问差不多,都是要将第二个参数设置成将n分解成k个数之和,只不过第二问对于这些数的要求是不同,第三问变成了奇数。这里选择开两个数组,一个记录偶数,一个记录奇数。对于ji[n][k],有两种情况,第一,有1,那么就扔掉1,剩下的数每个减一,变成ou[n-k][k-1],若没有一,这里容易写错,我刚刚写成的是ou[n-k][k-1]其实是不对的,因为可能有多个1,把第一个1扔掉之后不一定能够保证就没有,残余的1了,那么怎么样才是正确的写法呢,应该是ji[n-1][k-1],相当于把第一个1扔掉,剩下的部分进行奇数分解,同时由于能够保证剩下的n-1进行的分解也是从1开始的,符合我们对于子问题的定义,所以这个转移方程是正确的!至于偶数,那就很简单了,直接每个数减一,不用担心0的出现,直接就变成了ou[n-k][k],这样第三问也就解决啦!


下面贴上代码



#include<iostream>#include<cstring>using namespace std;int main(){int n, k;int num[60][60];int dif[60][60];int ji[60][60];int ou[60][60];while (cin >> n >> k){memset(num, 0, sizeof(num));memset(dif, 0, sizeof(dif));memset(ji, 0, sizeof(ji));memset(ou, 0, sizeof(ou));for (int i = 1; i <= n; ++i){num[i][1] = 1;}num[0][0] = 1;for (int i = 1; i <= n; ++i){for (int j = 2; j <= i; ++j){num[i][j] = num[i - j][j] + num[i - 1][j - 1];//divide i to j numbers(these numbers can be same)}}cout << num[n][k] << endl;//finish the first problemfor (int i = 1; i <= n; ++i){dif[i][1] = 1;}for (int i = 1; i <= n; ++i){for (int j = 2; j < i; ++j){dif[i][j] = dif[i - j][j - 1] + dif[i - j][j];}}int tmp = 0;for (int i = 1; i <= n; ++i){tmp += dif[n][i];}cout << tmp << endl;//now finish the second problemfor (int i = 1; i <= n; ++i){if (i % 2 == 1){ji[i][1] = 1;}else{ou[i][1] = 1;}ji[i][i] = 1;}for (int i = 1; i <= n; i++) {for (int j = 2; j < i; j++) {ji[i][j] = ji[i - 1][j - 1] + ou[i - j][j];ou[i][j] = ji[i - j][j];}}tmp = 0;for (int i = 1; i <= n; ++i){tmp += ji[n][i];}cout << tmp<<endl;//now finish the third problem}return 0;}



#include<iostream>#include<cstring>#include<queue>#include<string>using namespace std;int dp[51][51];int dif[51][51];int ji[60][60];int main(){int n, k;while (cin >> n >> k){memset(dp, 0, sizeof(dp));memset(dif, 0, sizeof(dif));memset(ji, 0, sizeof(ji));for (int i = 1; i <= n; ++i){dp[i][i] = 1;dp[i][1] = 1;}for (int i = 2; i <= n; ++i){for (int j = 2; j <= k; ++j){dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];}}cout << dp[n][k] << endl;//finish puzzle 1//finish puzzle 1for (int i = 2; i <= n; ++i){dif[i][1] = 0;}for (int i = 1; i <= n; ++i){dif[1][i] = 1;}for (int i = 1; i <= n; ++i){dif[0][i] = 1;}for (int i = 2; i <= n; ++i){for (int j = 2; j <= n; ++j){if (i >= j)dif[i][j] = dif[i - j][j - 1] + dif[i][j - 1];else{dif[i][j] = dif[i][i];}}}cout << dif[n][n] << endl;//finish puzzle 2for (int i = 1; i <= n; ++i){ji[i][1] = 1;ji[0][i] = 1;}for (int i = 1; i <= n; ++i){for (int j = 2; j <= n; ++j){if (i >= j){if (j % 2 == 0){ji[i][j] = ji[i][j - 1];}else{ji[i][j] = ji[i - j][j] + ji[i][j - 2];}}else{ji[i][j] = ji[i][i];}}}cout << ji[n][n] << endl;}return 0;}

原创粉丝点击