1 动态规划问题中的划分问题
动态规划问题中的划分问题是确定一个给定的集是否可以划分为两个子集,使得两个子集中的元素之和相同。
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果。
2 应用示例
动态规划问题中的划分问题示例:
arr[]={1,5,11,5}
输出:真
数组可以划分为{1、5、5}和{11}
arr[]={1,5,3}
输出:false
数组不能划分为等和集。
以下是解决此问题的两个主要步骤:
1) 计算数组的和。如果sum为奇数,则不可能有两个sum相等的子集,因此返回false。
2) 如果数组元素之和为偶数,则计算sum/2并找到sum等于sum/2的数组子集。
第一步很简单。第二步很关键,可以使用递归或动态规划来解决。
递归解决方案
下面是上面提到的第二步的递归属性。
让isSubsetSum(arr,n,sum/2)为函数,如果
有一个子集arr[0..n-1],其和等于和/2isSubsetSum问题可分为两个子问题
a) IsubSetSum()不考虑最后一个元素(将n减至n-1)
b) isSubsetSum考虑最后一个元素(通过arr[n-1]和n到n-1减少sum/2)
如果上述任何子问题返回true,则返回true。
isSubsetSum(arr,n,sum/2)=isSubsetSum(arr,n-1,sum/2)|| isSubsetSum(arr,n-1,sum/2-arr[n-1])
3 源代码
using System;
using System.Collections;
using System.Collections.Generic;
namespace Legalsoft.Truffer.Algorithm
{
public static partial class Algorithm_Gallery
{
#region 算法1
private static bool Partition_Problem_IsSubset_Sum(int[] arr, int n, int sum)
{
if (sum == 0)
{
return true;
}
if (n == 0 && sum != 0)
{
return false;
}
if (arr[n - 1] > sum)
{
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum);
}
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum) || Partition_Problem_IsSubset_Sum(arr, n - 1, sum - arr[n - 1]);
}
static bool Partition_Problem_Solve(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
return Partition_Problem_IsSubset_Sum(arr, n, sum / 2);
}
#endregion
#region 算法2
private static bool Partition_Problem_IsSubset_Sum(int[] arr, int n, int sum, int[,] dp)
{
if (sum == 0)
{
return true;
}
if (n == 0 && sum != 0)
{
return false;
}
if (dp[n, sum] != -1)
{
return true;
}
if (arr[n - 1] > sum)
{
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum, dp);
}
dp[n, sum] = (Partition_Problem_IsSubset_Sum(arr, n - 1, sum, dp) || Partition_Problem_IsSubset_Sum(arr, n - 1, sum - arr[n - 1], dp)) ? 1 : -1;
return dp[n, sum] > 0;
}
public static bool Partition_Problem_Solve_Second(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
int[,] dp = new int[n + 1, sum + 1];
return Partition_Problem_IsSubset_Sum(arr, n, sum / 2, dp);
}
#endregion
#region 算法3
public static bool Partition_Problem_Solve_Third(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
bool[,] part = new bool[sum / 2 + 1, n + 1];
for (int i = 0; i <= n; i++)
{
part[0, i] = true;
}
for (int i = 1; i <= sum / 2; i++)
{
part[i, 0] = false;
}
for (int i = 1; i <= sum / 2; i++)
{
for (int j = 1; j <= n; j++)
{
part[i, j] = part[i, j - 1];
if (i >= arr[j - 1])
{
part[i, j] = part[i, j - 1] || part[i - arr[j - 1], j - 1];
}
}
}
return part[sum / 2, n];
}
#endregion
#region 算法4
public static bool Partition_Problem_Solve_Fourth(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
bool[] part = new bool[sum / 2 + 1];
for (int i = 0; i <= sum / 2; i++)
{
part[i] = false;
}
for (int i = 0; i < n; i++)
{
for (int j = sum / 2; j >= arr[i]; j--)
{
if (part[j - arr[i]] == true || j == arr[i])
{
part[j] = true;
}
}
}
return part[sum / 2];
}
#endregion
}
}
4 源程序
using System;
using System.Collections;
using System.Collections.Generic;
namespace Legalsoft.Truffer.Algorithm
{
public static partial class Algorithm_Gallery
{
#region 算法1
private static bool Partition_Problem_IsSubset_Sum(int[] arr, int n, int sum)
{
if (sum == 0)
{
return true;
}
if (n == 0 && sum != 0)
{
return false;
}
if (arr[n - 1] > sum)
{
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum);
}
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum) || Partition_Problem_IsSubset_Sum(arr, n - 1, sum - arr[n - 1]);
}
static bool Partition_Problem_Solve(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
return Partition_Problem_IsSubset_Sum(arr, n, sum / 2);
}
#endregion
#region 算法2
private static bool Partition_Problem_IsSubset_Sum(int[] arr, int n, int sum, int[,] dp)
{
if (sum == 0)
{
return true;
}
if (n == 0 && sum != 0)
{
return false;
}
if (dp[n, sum] != -1)
{
return true;
}
if (arr[n - 1] > sum)
{
return Partition_Problem_IsSubset_Sum(arr, n - 1, sum, dp);
}
dp[n, sum] = (Partition_Problem_IsSubset_Sum(arr, n - 1, sum, dp) || Partition_Problem_IsSubset_Sum(arr, n - 1, sum - arr[n - 1], dp)) ? 1 : -1;
return dp[n, sum] > 0;
}
public static bool Partition_Problem_Solve_Second(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
int[,] dp = new int[n + 1, sum + 1];
return Partition_Problem_IsSubset_Sum(arr, n, sum / 2, dp);
}
#endregion
#region 算法3
public static bool Partition_Problem_Solve_Third(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
bool[,] part = new bool[sum / 2 + 1, n + 1];
for (int i = 0; i <= n; i++)
{
part[0, i] = true;
}
for (int i = 1; i <= sum / 2; i++)
{
part[i, 0] = false;
}
for (int i = 1; i <= sum / 2; i++)
{
for (int j = 1; j <= n; j++)
{
part[i, j] = part[i, j - 1];
if (i >= arr[j - 1])
{
part[i, j] = part[i, j - 1] || part[i - arr[j - 1], j - 1];
}
}
}
return part[sum / 2, n];
}
#endregion
#region 算法4
public static bool Partition_Problem_Solve_Fourth(int[] arr, int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += arr[i];
}
if (sum % 2 != 0)
{
return false;
}
bool[] part = new bool[sum / 2 + 1];
for (int i = 0; i <= sum / 2; i++)
{
part[i] = false;
}
for (int i = 0; i < n; i++)
{
for (int j = sum / 2; j >= arr[i]; j--)
{
if (part[j - arr[i]] == true || j == arr[i])
{
part[j] = true;
}
}
}
return part[sum / 2];
}
#endregion
}
}