算法作业-集合划分-回溯法

给定一个整数集合S,求集合S的一个划分S1和S2(即:S=S1∪S2且S1∩S2=Φ),使得S1中的元素之和等于S2中的元素之和。
1),证明此问题是NP难的。

2),设计一个搜索算法(回溯法或分支界限法)求解此问题。

解:
1)证明:由题目可知,只需要证明该集合划分问题是NP完全问题即可。为了证明任意一个问题A是NP完全问题,需要做到下面四个步骤。
1,存在一个非确定性多项式时间算法能解决A,例如A∈NP;
2,任意NP完全问题B能简化为A;
3,由B到A的简化在多项式时间里可以完成;
4,当且仅当B有解时原始问题A有解。
现在来证明集合划分问题是NP完全的。
1,SET-PARTITION∈NP:假设两个划分,证明这两个划分有相同的元素之和;
2,子集和问题到集合划分问题的简化:子集和问题定义如下:给定一个整数集合X和目标值t,找到一个子集Y⊆X使得集合Y中的成员之和为t。假设s为X中的成员之和。将X’= X∪{s – 2t}加入集合划分中;
3,这个简化可在多项式时间内完成;
4,只需证明当且仅当<X’>∈集合划分问题时<X,t>∈子集和问题。X’的成员之和为2s-2t;
如果X中存在一个子集,其和为t,那么X中剩余成员之和为s-t。因此,存在X’的一个划分使得划分的两个子集之和均为s-t。如果存在一个X’的划分使得划分的两个子集之和均为s-t。两个子集之一包含数字s – 2t。移除这个数字,我们得到一个子集,其和为t,并且所有子集成员均属于集合X。

证毕!

2)回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。

从元素在与不在子集这两种状态来考虑,该解空间树为一棵二叉树,如下图:

Java代码如下:

package sum_subset;

public class Traceback {
    
    public int getSum1(boolean[] visited, int[] A) {
        int sum = 0;
        for(int i = 0;i < A.length;i++) {
            if(visited[i])
                sum += A[i];
        }
        return sum;
    }
    
    public void getSubSet1(boolean[] visited, int[] A, int m, int step) {
        if(step == A.length) {
            if(getSum1(visited, A) == m) {
                for(int i = 0;i < A.length;i++) {
                    if(visited[i])
                        System.out.print(A[i]+" ");
                }
                System.out.print("和");
                for(int i = 0;i < A.length;i++) {
                    if(!visited[i])
                        System.out.print(A[i]+" ");
                }
                System.out.println();
            }
            return;
        }
        visited[step] = true;
        getSubSet1(visited, A, m, step + 1);
        visited[step] = false;
        getSubSet1(visited, A, m, step + 1);
    }
    
    public static void main(String[] args) {
    	Traceback test = new Traceback();
        int[] A = new int[8];
        int sumA = 0;
        boolean[] visited = new boolean[8];
        for(int i = 0;i < 8;i++) {
            A[i] = i + 1;
            sumA = sumA + A[i];
            visited[i] = false;
        }
        if(sumA%2 == 0){
        	test.getSubSet1(visited, A, sumA/2, 0);
        }
        else {
        	System.out.println("无法找到正确的解!");
		}
    }
}
用三个整数集合对代码进行实例测试的结果如下:

1,整数集合为{1,2,3,4,5,6,7,8}时,输出结果为:


每行的数字代表S1和S2集合中的元素。

2,整数集合为{1,2,3,4,5,6,7,8,9}时,输出结果为:


猜你喜欢

转载自blog.csdn.net/qq_36309480/article/details/80572688
今日推荐