poj 1221 单峰回文数

qwertyxk1221Accepted888K0MSC++798B2013-01-14 12:01:00

先来看分析

这个题目和昨天写的3181题目是相同的,也是一个整数划分问题,于是我们可以把那个dp的方法运用过来

这个题目需要做一点点变形,分奇数和偶数来讨论

POJ 1221 的大意是:回文序列是指从左向右读和从右向左读都是一样的序列。而单峰回文序列则是该序列从左和从右向中间单调非递减的序列。例如,题号 1 2 2 1 就是一个单峰回文序列,如果把每个数字看成序列的元素。现在,给定整数 n ,求 n 的单峰回文序列划分的个数。

首先,我们注意到,回文序列有两中:序列的长度为奇数和偶数的回文序列。显然,奇数 n 只可能有长度为奇数的单峰回文划分,而偶数则可以有长度为奇数的回文划分,也可以有长度为偶数的回文划分。我们先看长度为奇数的回文划分,因为这是所有整数都可以有的。

对于长度为奇数的回文划分,如果两个序列的中间的元素值不同,则两个划分肯定不同(这个不难证明)。那么,当我们固定中间元素的值为 v 时,也就是固定了中间元素左边(或右边)的序列之和 ( s = (n-v) /2,这里,取 v 使得 s 为整数 ) 时,有多少种不同的单峰回文划分呢?为了使得中间元素为 v 的回文划分是单峰的,则 s 的划分中所有元素都不能比 v 大。反过来,s 的每一个最大元素不超 v 的划分,都可以和 v 一起构成 n 的一个单峰回文划分!因此,问题就变成了求 f(s,v) !对所有可能的 (s,v) 求 f(s,v) 之和,则为 n 的长度为奇数的单峰回文划分的个数。

这样,当 n 为奇数时,问题已经得到解决。当 n 为偶数时,可以有长度为偶数的单峰回文划分。这些划分的左边之和等于右边等于 n/2。整数 s = n / 2 的每个不同的划分都可以用来构成 n 的一个不同单峰回文划分,因此,长度为偶数的单峰回文划分的个数为 f(s,s)。至此,POJ 1221 得到完满解决,而核心问题则是解决整数的划分,即求 f(n,m)。

这个f(n,m)已经得到完整的解决,参考 http://blog.csdn.net/qwerty_xk/article/details/8499011


源代码如下:

#include<stdio.h>
#include<string.h>
__int64 dp[300][300]; 
int min(int a,int b)
{
	return a<b?a:b;
}
int main()
{
	int n,i,j;
	memset(dp,0,sizeof(dp));
	for(i=0;i<300;i++)  //先对以后要用到的dp都求出来,在数组中保存好
		dp[0][i]=1;
	for(i=1;i<300;i++)
	{
		for(j=1;j<300;j++)
		{
			if(j-i<0)
			{
				dp[j][i]=dp[j][i-1];
				continue;
			}
			dp[j][i]=dp[j][i-1]+dp[j-i][i];
		}
	}
	while(scanf("%d",&n))
	{
		if(n==0)
			break;
		__int64 sum=0;
		if(n%2!=0) //奇数时候的情形
		{
			for(int v=1;v<n;v++)
			{
				if((n-v)%2==0) //要能做到左右对称,故必须能整除
				{
					int s=(n-v)/2;
					sum+=dp[s][v];
				}
			}
		}
		else //偶数时候的情形
		{
			int s;
			for(int v=1;v<n;v++)  //分奇偶数列来进行讨论
			{
				if((n-v)%2==0)
				{
					s=(n-v)/2;
					sum+=dp[s][v];
				}
			}
			s=n/2;  //偶数序列
			sum+=dp[s][s];
		}
		sum+=1; //最后别忘了把自己本身这个数加上
		printf("%d %I64d\n", n,sum);
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值