动态规划问题详解(二)

  在动态规则问题详解(一)中,我们用1,3,5三个硬币,求用最少的硬币组成11元的问题。这一节,我们来讨论一个复杂一点的问题。这个问题,曾经是某公司的面试题。

   有从1-N个连接的整数,把1-N个整个划分为二个相等的集合,问有几种划分的方法。

       如  {1,2,3},则有{1,2}和{3},只有一种划分方法。

       如{1,2,3,4,5,6,7} 则有

 {1,6,7} and {2,3,4,5} {注 1+6+7=2+3+4+5}
{2,5,7} and {1,3,4,6}
{3,4,7} and {1,2,5,6}
{1,2,4,7} and {3,5,6}

这四种组合,编写程序,输入N,输入有几种划分的组合数。
如输入3,则输出1
输入7,则输出4
这个问题如何求解呢?
我们先分析,1-N个整数,和是多少? 根据等差数列的公式,我们得到 s=n*(1+n)/2,题目中要求把1-N划分为两个相等的集合,那么,s必须是偶数。 s%2 =0 . 如果s为奇数,则直接返回0.如果s为偶数,则,问题转化为,从1-N中取前i个数,使它们的和为s/2的组合。我们设d[i][j]为集合组和的个数,其中j为前i个数的和。那么,d[i][j]的状态方程如何写呢?
  d[i][j]为前i个数的和,按照动态规划的思想,d[i][j]的状态,只和i-1个数的状态有关。第i个数,要么在集合中,要么不在集合中(有人要问了,不是所有的数都要在集合中么?是的,共有两个集合,我们只拿其中一个集合来思考)
如果第i个数,在集合中,则第i-1个数的和为j.  d[i][j]=d[i-1][j].如果第i个数在集合中,则前i-1个数的和,为j-i.  d[i][j]=d[i-1][j-i]。
现在d[i][j]有两种情况,我们如何处理呢?之前我说过,本来共有2个集合,但为了思考的方便,我们只考虑了一个集合的情况.第i个数,要么在第一个集合中,要么在第二个集合中。
如{1,2,3} 可以划分成{1,2}及{3} 也可以划分为{3}及{1,2},虽然在本题中,这两种情况只算一种。但这个对我们后面结果数除以2的理解是有帮助的。
我们把这两种情况相加,然后除以2。
d[i][j]=d[i-1][j]+d[i-1][j-i]
有了这个状态转移方程,我们就可以很方便的写出代码了。


#include <stdio.h>
void main()
{
  int N=3;
  int way = divide(N);
  printf("%d\n", way);
}

int divide(int N)
{
  int d[1024]={0};
  int total=N*(N+1)/4;
  if (total*2 % 2)
  {
    return 0;
  }
  int i;
  int j;
  d[0]=1;
  for (i=1;i<=N;i++)
  {
    for (j=total;j>=i;j--)
    {
      d[j]= d[j] + d[j-i];
    }
  }
  return d[total]/2;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值