dynamic programming resolve the partition problem

dynamic programming resolve the partition problem

partition is a decision problem which was defined as following:

instance: a set A = {a1, a2, .., an} of n nonegative integers.
question: is there a subset B such that the sum of B is equal to A-B?

the brute-force algorithm that tries every prossibles subset consumes no less than 2exp(n) time.

the dynamic programming for partition problem uses O(n*sum) time.

the recurrence relation
if sum odd, reply "no", and done;
let table(i, j) denote the truth value of the statement
    "there is a subset of (a1, a2, .. , aj} for which the sum of integers is exactly j", where 1<= i <= n and 0 <= j <= sum/2;

we are asked to compute table(n, sum/2)

we derive:
the general recurrence table(i, j) = t(i-1, j) U table(i-1, j-ai),with the base value:
1. table(i, j) = 1, if j==0;
2. table(i, j) = 1, if i==1 and j = a1;
3. table(i, j) = 0, 0<j!=a1 and i = 1;
then we use the base value to computer the following value ,when we fill the table,the last value indicate there is a subset satisfy the condition or not.

the c++ code write for this question as following.

the header file:
#pragma once
#ifndef _SUBSET_H
#define _SEBSET_H

typedef struct _SetDesc
{
public:
unsigned int *pUintSet;
unsigned int setCount;
}SSetDesc;
class subSet
{
private:
//
//set description
SSetDesc m_set;
//
//set for result
SSetDesc m_subSet;
public:
subSet(void);
subSet(unsigned int *uintSet, unsigned int setCount);
subSet(SSetDesc uintSet);
//
//if the return value is NULL, it indicate there is not a subset in accordance with you require

SSetDesc * result( );
~subSet(void);
};
#endif


the implement code:
#include "StdAfx.h"
#include "subSet.h"

subSet::subSet( void )
{
}
subSet::subSet( unsigned int *uintSet, unsigned int setCount )
{
if( uintSet )
{
  m_set.pUintSet = new unsigned int[ setCount ];

  memcpy( m_set.pUintSet, uintSet, setCount*sizeof( unsigned int ) );
  m_set.setCount = setCount;
}

printf( "there are %d elements:/n", m_set.setCount );
for( unsigned int i = 0; i < setCount; ++i )
{
  printf( "%d/t", *( m_set.pUintSet + i ) );
}
printf("/n");
}
subSet::subSet( SSetDesc uintSet )
{
if( uintSet.pUintSet )
{
  m_set.pUintSet = new unsigned int[ uintSet.setCount ];

  memcpy( m_set.pUintSet, uintSet.pUintSet, uintSet.setCount*sizeof(unsigned int) );
  m_set.setCount = uintSet.setCount;
}
}
SSetDesc * subSet::result( )
{
//
//此处为算法实现部分

unsigned int sum = 0;
for(unsigned i = 0; i < m_set.setCount; ++i)
{
  sum += *( m_set.pUintSet + i );
}

//
//如果和为奇数,则不存在, pass
if( 1 == sum%2 )
{
  return NULL;
}

//
//构建一个虚拟化二维数组
unsigned int *table = new unsigned int[ m_set.setCount*( 1 + sum/2 ) ];
memset( ( void * )table, 0, m_set.setCount*( 1 + sum/2 )*sizeof( unsigned int ) );

printf("打印每一行/n");
for( int i = 0; i<m_set.setCount; ++i )
{
  printf("/n");
  for( int j=0; j<sum/2+1; ++j )
  {
   printf("%d/t", *(table + i*(1 + sum/2) + j) );
  }
}
printf("/n");

//置表格第一列为1
for( unsigned int i = 0; i < m_set.setCount; ++i )
{
  *( table + i*( 1+ sum/2 ) ) = 1;
}
//置表格第一行中列序号==第一个元素值的表格单元 1
for( unsigned int i = 0; i < ( 1 + sum/2 ); ++i )
{
  if( i == *( m_set.pUintSet ) )
   *( table + i ) = 1;
}

//
//
//table[ i ][ j ] = table[ i-1 ][ j ] U table[ i-1 ][ j-a
//table[ i-1 ][ j-a ] == 1意味着在不加入元素a[ i ]的情况下存在
//满足条件的集合
//
for( unsigned int i = 1; i < m_set.setCount; ++i )
{
  for( unsigned int j = 0; j < 1 + sum/2; ++j )
  {
   //若某一元素为1,则表格中它下面的所有元素都为1
   if( 1 == *( table + ( i - 1 )*( 1 + sum/2 ) + j ) )
   {
    *( table + i*( 1 + sum/2 ) + j ) = 1;
    continue;
   }

   //
   // *( table + ( i - 1 )*( 1 + sum/2 ) + j - *( m_set.pUintSet + i ) )
   // 保证数组中列序号不为负
   // 晕,在这里出错,MS编译器有一个小bug,if(无符数相减如果为负)
   // if( ( j - *( m_set.pUintSet + i ) ) >= 0 )会出错,因为都是无符号数
   // 相减为负时会溢出
   //

   if( j >= *( m_set.pUintSet + i ) )
   {
    if( 1 == *( table + ( i - 1 )*( 1 + sum/2 ) + j - *( m_set.pUintSet + i ) ) )
    {
     *( table + i*( 1 + sum/2 ) + j ) = 1;
    }
   }
  }
}

//
//最后一个元素为1,则存在这样的集合m_set.setCount*( 1 + sum/2 )
if( 1 == *( table + m_set.setCount*( sum/2 +1 ) - 1 ) )
{
  unsigned int i = m_set.setCount - 1;
  //给j赋初值
  unsigned int j = sum/2;

  printf("/n打印出子集:/n");
  while( i >= 0 && 1 == *( table + i*( sum/2 +1 ) + j ) )
  {
   if( 1 == *( table + ( i - 1 )*( sum/2 +1 ) + j ) )
   {
    //
    //如果它上面的元素也为1,向上回溯
    --i;
   }
   else
   {
    if( j != 0 )
     printf("%d/t", *( m_set.pUintSet + i ) );

    j = j - *( m_set.pUintSet + i );

    --i;
   }
  }
}

/*for( unsigned int i = m_set.setCount - 1 ; i >= 0; --i )
{
  for( unsigned int j = sum/2; j >= 0; --j)
  {

  }
}*/
printf("/n打印最后表格/n");
for( int i = 0; i<m_set.setCount; ++i )
{
  printf("/n");
  for( int j=0; j<sum/2+1; ++j )
  {
   printf("%d   ", *(table + i*(1 + sum/2) + j) );
  }
}

if( 1 == *( table + ( m_set.setCount - 1)*( 1 + sum/2 ) + sum/2 ) )
  printf( "/nthere is a subset satisfy the condition" );
else

delete table;


return NULL;

}
subSet::~subSet( void )
{
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值