算法:科克曼女生问题的一种解法

来源:互联网 发布:大数据公司销售好干吗 编辑:程序博客网 时间:2024/05/16 19:19

关于科克曼女生问题见http://baike.baidu.com/view/80040.htm详述,以下具体谈到一些算法方面问题。

1850年,科克曼(Kirkman)在《女士与先生之日记》杂志上发表了题为的文章,提出了15个女学生问题:

某寄宿学校的15个学生,每天都要3人一行的外出散步一次,怎样安排才能使得每个女生7天内核其他14个女生散步各一次。

  • 初步分析估计:通过思考分析,7天,每天有5组,每组3人,七天就总共35组,35组肯定不可能出现两组是完全一样的搭配,那么实际上可能的组也就是一个15选3的一个无序排列组合,也就是455个可能。
  • 第一步:那么这455个可能中,为了让每个女生都能够与其他女生都有结对过,那么最优的情况是看从中选取N组,使得这N组中,任意两组之间的交集不超过1,也就是不可能有两组是(A,B,C)与(A,B,D)在一起,那么这个也就是得通过设计一个简单的算法计算出这个N组为多少,而意外的是发现这个N=35,也就是说正好满足7天的安排,并且满足大组内全见面的条件。
  • 第二步:这样通过算法得到了一个35个组,每组3个的一个列表,本以为问题就差不多,剩下的就是35组按条件拆分为7天的组合,保证每天内,每个人只出现在一个小组就行,后来发现这一步也不是那么简单,无回溯方式的探测,发现并不能将35组等分为7天,而是十天的组合,有的一天内只有三个小组,随后也就发现,这里的探测选取,有些支路并不符合,可能会导致走入死胡同,因此,应当记录探测分支,不符合的分支应当记录,并回溯,走其他分支进行探测。最终欣喜的得出一个答案:

( 1 2 3 ) ( 4 8 12 ) ( 5 10 15 ) ( 6 11 13 ) ( 7 9 14 ) 
( 1 4 5 ) ( 2 9 11 ) ( 3 12 15 ) ( 6 8 14 ) ( 7 10 13 ) 
( 1 6 7 ) ( 2 8 10 ) ( 3 13 14 ) ( 4 11 15 ) ( 5 9 12 ) 
( 1 8 9 ) ( 2 13 15 ) ( 3 4 7 ) ( 5 11 14 ) ( 6 10 12 ) 
( 1 10 11 ) ( 2 12 14 ) ( 3 5 6 ) ( 4 9 13 ) ( 7 8 15 ) 
( 1 12 13 ) ( 2 5 7 ) ( 3 8 11 ) ( 4 10 14 ) ( 6 9 15 ) 
( 1 14 15 ) ( 2 4 6 ) ( 3 9 10 ) ( 5 8 13 ) ( 7 11 12 )

本来就感觉这个应该是一个多解的问题(不是表面交换,而且搭配也会有变化),在做出来之后,参考百度百科,发现“西尔维斯特问题"更加抽象的概括这一问题,并且对于v=15,有13个不同的解。
那么对于另外12个解,个人就没有太多精力去全探测出来,但是从思路上判断,应该是第一步和第二部中的探测寻找选择起点位置进行更换(个人实现的算法中简化处理,找到一组解就行),这样可以得到解法的其他分支,并得到最后的结果。
最后个人的Java算法实现源码如下:
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package puzzle;
 
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
 
/**
 * Class
 *
 * @author Vanjor
 * @site http://www.vanjor.org
 *
 */
public class PPair {
 
    private HashSet<Integer> pair = new HashSet<Integer>();
 
    public PPair(int x, int y, int z) {
        pair.add(x);
        pair.add(y);
        pair.add(z);
    }
 
    public HashSet<Integer> getPair() {
        return pair;
    }
 
    /**
     * 判定两个Pair交集不大于1
     */
    public boolean isSimilar(PPair p) {
        Set<Integer> set = new HashSet<Integer>();
        set.addAll(p.getPair());
        set.addAll(pair);
        if (set.size() > 4) {
            return false;
        } else {
            return true;
        }
    }
 
    /**
     * 判断当前Pair与container是否有交集,有交集则返回false,无交集则返回true
     *
     * @param container
     * @return
     */
    public boolean isTotalDifferent(HashSet<Integer> container) {
        for (Integer index : pair) {
            if (container.contains(index)) {
                return false;
            }
        }
        return true;
    }
 
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("( ");
        for (Integer mm : pair) {
            sb.append(mm).append(" ");
        }
        sb.append(")");
        return sb.toString();
    }
 
    public static final int SIZE = 15;
 
    public static LinkedList<PPair> getPairList() {
        // 最原始的随机组合共455个
        LinkedList<PPair> originaList = new LinkedList<PPair>();
        // Pair交集不大于1的值剩下35个,正好为7*15
        LinkedList<PPair> presentList = new LinkedList<PPair>();
        for (int index1 = 1; index1 <= SIZE - 2; index1++) {
            for (int index2 = index1 + 1; index2 <= SIZE - 1; index2++) {
                for (int index3 = index2 + 1; index3 <= SIZE; index3++) {
                    originaList.add(new PPair(index1, index2, index3));
                }
            }
        }
 
        PPair seed = null;
        while (!originaList.isEmpty()) {
            seed = originaList.remove();
            presentList.add(seed);
            LinkedList<PPair> tempList = new LinkedList<PPair>();
            for (PPair index : originaList) {
                if (seed.isSimilar(index)) {
                    tempList.add(index);
                }
            }
            originaList.removeAll(tempList);
        }
        return presentList;
    }
 
    public static LinkedList<LinkedList<PPair>> arrangeList(
            LinkedList<PPair> list) {
        // 结果7天的排列
        LinkedList<LinkedList<PPair>> result = new LinkedList<LinkedList<PPair>>();
 
        while (!list.isEmpty()) {
            LinkedList<PPair> tempList = new LinkedList<PPair>();
            HashSet<Integer> dailyArrange = new HashSet<Integer>();
 
            // 如果当次寻找不匹配,则跟据分支记录点回溯,另行分支再找
            HashSet<PPair> failedArchives = new HashSet<PPair>();
 
            // 每天为5组pair,保证15人全部出行
            while (tempList.size() < 5) {
                PPair branchRecord = null;
                for (PPair seed : list) {
                    if (!failedArchives.contains(seed)
                            && seed.isTotalDifferent(dailyArrange)) {
                        // 在分支尝试的时候,记住当前的分支
                        if (tempList.size() == 1) {
                            branchRecord = seed;
                        }
                        tempList.add(seed);
                        dailyArrange.addAll(seed.getPair());
                    }
                }
                // 若本分支寻找配对失败,记录配对失败的Pair,重置其他记录状态
                // 下次从其他分支开始尝试
                if (tempList.size() < 5) {
                    tempList = new LinkedList<PPair>();
                    dailyArrange = new HashSet<Integer>();
                    failedArchives.add(branchRecord);
                }
            }
            list.removeAll(tempList);
            result.add(tempList);
        }
        return result;
    }
 
    public static void main(String[] args) {
        LinkedList<PPair> pairList = getPairList();
        LinkedList<LinkedList<PPair>> result = arrangeList(pairList);
        // 打印结果
        for (LinkedList<PPair> row : result) {
            for (PPair pop : row) {
                System.out.print(pop.toString() + " ");
            }
            System.out.println();
        }
    }
}

原创粉丝点击