DP18 分割区间问题 Partition problem @geeksforgeeks

思路:

把能否划分区间的问题转化为能不能找到一个使得和为总和的一半的子区间,然后转为01背包问题,分析每一个元素是否取

Partition problem is to determine whether a given set can be partitioned into two subsets such that the sum of elements in both subsets is same.

Examples

arr[] = {1, 5, 11, 5}
Output: true 
The array can be partitioned as {1, 5, 5} and {11}

arr[] = {1, 5, 3}
Output: false 
The array cannot be partitioned into equal sum sets.

Following are the two main steps to solve this problem:
1) Calculate sum of the array. If sum is odd, there can not be two subsets with equal sum, so return false.
2) If sum of array elements is even, calculate sum/2 and find a subset of array with sum equal to sum/2.

The first step is simple. The second step is crucial, it can be solved either using recursion or Dynamic Programming.

Recursive Solution
Following is the recursive property of the second step mentioned above.

Let isSubsetSum(arr, n, sum/2) be the function that returns true if 
there is a subset of arr[0..n-1] with sum equal to sum/2

The isSubsetSum problem can be divided into two subproblems
 a) isSubsetSum() without considering last element 
    (reducing n to n-1)
 b) isSubsetSum considering the last element 
    (reducing sum/2 by arr[n-1] and n to n-1)
If any of the above the above subproblems return true, then return true. 
isSubsetSum (arr, n, sum/2) = isSubsetSum (arr, n-1, sum/2) ||
                              isSubsetSum (arr, n-1, sum/2 - arr[n-1])

package DP;

public class PartitionProbelm {

	public static void main(String[] args) {
		int[] A = {3, 1, 5, 9, 12};
		int n = A.length;
		System.out.println(findPartitionRec(A, n));
		System.out.println(findPartitionDP(A, n));
	}

	// A utility function that returns true if there is a subset of arr[]
	// with sun equal to given sum
	public static boolean isSubsetSumRec(int[] A, int n, int sum){
		if(sum < 0){
			return false;
		}
		if(sum == 0){
			return true;
		}
		if(n==0 && sum>0){
			return false;
		}
//		if(A[n-1] > sum){		// If last element is greater than sum, then ignore it
//			return isSubsetSumRec(A, n-1, sum);
//		}
		
		/* else, check if sum can be obtained by any of the following
	      (a) excluding the last element 不选A[n-1]
	      (b) including the last element	选A[n-1]
	   */
		return isSubsetSumRec(A, n-1, sum) || isSubsetSumRec(A, n-1, sum-A[n-1]);
	}

	// Returns true if A[] can be partitioned in two subsets of
	// equal sum, otherwise false
	//	Time Complexity: O(2^n) In worst case
	//	, this solution tries two possibilities (whether to include or exclude) for every element.
	public static boolean findPartitionRec(int[] A, int n){
		int sum = 0;		// Calculate sum of the elements in array
		for(int i=0; i<n; i++){
			sum += A[i];
		}
		if(sum%2 != 0){	// If sum is odd, there cannot be two subsets with equal sum
			return false;
		}
		
		// Find if there is subset with sum equal to half of total sum
		return isSubsetSumRec(A, n, sum/2);
	}
	
	// Returns true if A[] can be partitioned in two subsets of
	// equal sum, otherwise false
//	Time Complexity: O(sum*n)
//	Auxiliary Space: O(sum*n)
	public static boolean findPartitionDP(int[] A, int n){
		int sum = 0;
		for(int i=0; i<n; i++){	// Calculate sum of all elements
			sum += A[i];
		}
		if(sum%2 != 0){	//	If odd, then no chance
			return false;
		}
		
		// part[i][j]: 记录在长为j的数组里能否找到总和为i的子区间
		boolean[][] part = new boolean[sum/2+1][n+1];
		for(int i=0; i<=n; i++){		// initialize top row as true
			part[0][i] = true;		// 如果总和为0,则肯定能找到(即一个都不选)
		}
		for(int i=1; i<=sum/2; i++){	// initialize leftmost column, except part[0][0], as false
			part[i][0] = false;	// 如果总和为i,且数组长为0,则不可能找到
		}
		
		 // Fill the partition table in bottom up manner 
		for(int i=1; i<=sum/2; i++){	// 要找总和为i
			for(int j=1; j<=n; j++){	// 给定数组长度为j
				part[i][j] = part[i][j-1]; // 不选A[j-1]的情况,更新数组长度为j-1
				if(i-A[j-1] >= 0){		// 选择A[j-1]的情况,更新总和为i-A[j-1],也更新数组长度为j-1
					part[i][j] = part[i][j] || part[i-A[j-1]][j-1];
				}
			}
		}
		return part[sum/2][n];
	}
	
}


http://www.geeksforgeeks.org/dynamic-programming-set-18-partition-problem/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最小最大均衡连通q分割问题(Minimum Maximal Balanced Connected q-Cut Problem)是一个非常经典的图论问题,它的目标是将一个无向图分割成 q 个连通子图,使得每个子图的大小尽量均衡,同时每个子图之间的连通性尽量强,即被分割后的子图之间的边权和最小。 下面是一个 Python 代码示例,使用了 NetworkX 库来处理图论问题,使用 PuLP 库来求解线性规划问题: ```python import networkx as nx from pulp import * # 加载图 G = nx.Graph() G.add_edges_from([(1, 2), (1, 3), (2, 3), (2, 4), (3, 4), (4, 5), (5, 6), (6, 7)]) # 参数设置 q = 3 # 分割成 3 个子图 inf = float('inf') # 定义线性规划问题 prob = LpProblem("Minimum Maximal Balanced Connected q-Cut Problem", LpMinimize) # 定义变量 x = LpVariable.dicts("x", G.edges(), lowBound=0, upBound=1, cat=LpInteger) # 定义目标函数 prob += lpSum([x[e] for e in G.edges()]) # 定义约束条件 for i in range(q): prob += lpSum([x[e] for e in G.edges() if e[0] in range(i * len(G) // q, (i + 1) * len(G) // q)]) >= 1 prob += lpSum([x[e] for e in G.edges() if e[1] in range(i * len(G) // q, (i + 1) * len(G) // q)]) >= 1 for (u, v) in G.edges(): prob += x[(u, v)] + x[(v, u)] <= 1 prob += x[(u, v)] + x[(v, u)] >= 2 - q # 求解线性规划问题 prob.solve() # 输出结果 print("Minimum Maximal Balanced Connected q-Cut Problem:") print("Objective value:", value(prob.objective)) for i in range(q): print("Partition", i + 1, ":", [u for u in G.nodes() if value(x[(u, v)]) >= 0.99 or value(x[(v, u)]) >= 0.99 for v in G.nodes()]) ``` 上述代码中,我们首先使用 NetworkX 库构建了一个无向图,然后使用 PuLP 库定义了线性规划问题,并对其进行求解。具体来说,我们首先定义了变量 x[e],表示边 e 是否被切断,然后定义了目标函数为所有被切断的边的数量之和,同时定义了一些约束条件,确保分割后的子图数量为 q,每个子图都是连通的,并且每个子图的大小尽量均衡。最后,我们使用 prob.solve() 求解线性规划问题,并输出结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值