您的位置:首页 > 编程语言

程序设计实习MOOC/第十三周编程作业/C:UNIMODAL PALINDROMIC DECOMPOSITIONS(Greater New York 2002)

2014-05-20 20:32 781 查看
题目:C:UNIMODAL PALINDROMIC DECOMPOSITIONS
总时间限制: 1000ms 内存限制: 65536kB
描述
A sequence of positive integers is Palindromic if it reads the same forward and backward. For example:
23 11 15 1 37 37 1 15 11 23
1 1 2 3 4 7 7 10 7 7 4 3 2 1 1
A Palindromic sequence is Unimodal Palindromic if the values do not decrease up to the middle value and then (since the sequence is palindromic) do not increase from the middle to the end For example, the first example sequence above is NOT Unimodal Palindromic while the second example is.
A Unimodal Palindromic sequence is a Unimodal Palindromic Decomposition of an integer N, if the sum of the integers in the sequence is N. For example, all of the Unimodal Palindromic Decompositions of the first few integers are given below:
1: (1)
2: (2), (1 1)
3: (3), (1 1 1)
4: (4), (1 2 1), (2 2), (1 1 1 1)
5: (5), (1 3 1), (1 1 1 1 1)
6: (6), (1 4 1), (2 2 2), (1 1 2 1 1), (3 3),
(1 2 2 1), ( 1 1 1 1 1 1)
7: (7), (1 5 1), (2 3 2), (1 1 3 1 1), (1 1 1 1 1 1 1)
8: (8), (1 6 1), (2 4 2), (1 1 4 1 1), (1 2 2 2 1),
(1 1 1 2 1 1 1), ( 4 4), (1 3 3 1), (2 2 2 2),
(1 1 2 2 1 1), (1 1 1 1 1 1 1 1)
Write a program, which computes the number of Unimodal Palindromic Decompositions of an integer.
输入
Input consists of a sequence of positive integers, one per line ending with a 0 (zero) indicating the end.
输出
For each input value except the last, the output is a line containing the input value followed by a space, then the number of Unimodal Palindromic Decompositions of the input value. See the example on the next page.
样例输入
2
3
4
5
6
7
8
10
23
24
131
213
92
0
样例输出
2 2
3 2
4 4
5 3
6 7
7 5
8 11
10 17
23 104
24 199
131 5010688
213 1055852590
92 331143
提示
N < 250

解题思路:

dp[i][j]表示i分解成最大元素不超过j的整数之和的方案数(每种方案自然可以对应成一个不递减的序列)
类似于背包问题,它代表用1,2,3...j这些数字,每个数字可以取任意多个,使得和为i,有多少种方式
分解成子问题,即选取的j的个数确定时(设为k个),剩余整数之和i-k*j分解成最大元素不超过j-1的方案数之和
dp[i][j] = dp[i - 0*j][j - 1](j取0个,i分解成最大元素不超过j-1)
+ dp[i - 1*j][j - 1](j取1个,所以剩余的为i-j分解成最大元素不超过j-1)
+ dp[i - 2*j][j - 1](j取2个,所以剩余的为i-2*j分解成最大元素不超过j-1)
+ ... 一直加到"i - k*p < 0"结束

dp[i][i]求到的是i分解成最大元素不超过i的整数之和的方案数,即单调不递减序列
这里求的是单峰回文序列,即单调不递减再单调不递增,后面与前面是对应的,所以,
对于奇数i,只有一种情况,
枚举中间取值(会是奇数),设为mid,左边就是个单调不递减序列,
对于每一个mid值,都有方案数dp[(i-mid)/2][min((i-mid)/2, mid//有mid是确保mid前的元素不会超过mid,保持单调不递减)],
因为右边和左边相对应,所以左边每一种方案,右边都是唯一确定的,
如 7: (7), (1 5 1), (2 3 2), (1 1 3 1 1), (1 1 1 1 1 1 1)
mid 取 7,dp[(7-7)/2][min((7-7)/2, 7)] = 1//mid自己本身就是一种方案
mid 取 5,dp[(7-5)/2][min((7-5)/2, 5)] = 1
mid 取 3,dp[(7-3)/2][min((7-3)/2, 3)] = dp[2][2] = 2,即2可分为1 1和2,回文的,所以后面是唯一对应的
mid 取 1,dp[(7-1)/2][min((7-1)/2, 1)] = dp[3][1] = 1,即3分为1 1 1(最大元素不超过1),回文的,所以后面是唯一对应的

对于偶数i,分为两种情况,
如8:第一类(8), (1 6 1), (2 4 2), (1 1 4 1 1), (1 2 2 2 1),(1 1 1 2 1 1 1);
mid取8,6,4,2,对应dp[0][0] + dp[1][1] + dp[2][2] + dp[3][2] = 1 + 1 + 2 + 2
注意不能包含mid取0的情况,因为dp[0][0]等于1,多加了1会导致结果有问题
第二类(4 4), (1 3 3 1), (2 2 2 2),(1 1 2 2 1 1), (1 1 1 1 1 1 1 1)。
dp[i/2][i/2] = dp[4][4] = 5

代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
#define NUM 250

int main()
{
int dp[NUM][NUM];
for(int i = 0; i < NUM; i++)
{
dp[i][0] = 1;
dp[0][i] = 1;//注意初始化为1,是因为会有一种i仅仅由j组成序列的情况(如(4,2):4 = 2 + 2,(8,4):8 = 4 + 4)
//如dp[2][2] = dp[2 - 0][1] + dp[2 - 1*2][1](这里该是1,对应了"2"这种情况)
//又如dp[3][2] = dp[3][1] + dp[3 -1*2][1] = 1 + 1(是dp[1][1],也是一种情况,没有dp[0][1],所以不会额外多加了1)
//又如dp[4][2] = dp[4][1] + dp[4 - 1*2][1] + dp[4 - 2*2][1](这里该是1,对应了2个2,即"2 2"这种情况)
//又如dp[4][4] = dp[4][3] + dp[4 - 1*4][3](这里该是1,对应了1个4,即"4"这种情况)
//又如dp[6][4] = dp[6][3] + dp[6 - 1*4][3](是dp[2][3],也是一种情况,没有dp[0][3],所以不会额外多加了1)
}//设置边界状态的值
for(int i = 1; i < NUM; i++)
dp[i][1] = 1;//设置边界状态的值

for(int i = 1; i < NUM; i++)
{
for(int j = 2; j < NUM; j++)
{
dp[i][j] = dp[i - 0 * j][j - 1];
int k = 1;
while(i - k * j >= 0)
{
dp[i][j] += dp[i - k * j][j - 1];
k++;
}
}
}

/*
for(int i = 0; i < NUM; i++)
{
for(int j = 0; j < NUM; j++)
cout << dp[i][j] << " ";
cout <<endl;
}
*/

int i;
long long num;//数值太大,使用long long型,防止溢出
cin >> i;
while(i)
{
num = 0;
if(i % 2 == 1)//奇数
{
for(int mid = i; mid > 0; mid -= 2)
num += dp[(i - mid) / 2][min((i - mid) / 2, mid)];
}
else//偶数
{
for(int mid = i; mid > 0; mid -= 2)
num += dp[(i - mid) / 2][min((i - mid) / 2, mid)];
num += dp[(i/2)][(i/2)];
}

cout << i << " " << num <<endl;
cin >> i;
}

system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  动态规划
相关文章推荐