逻辑练习《6人参加会议》

源于《三个变态的循环》第一题

原文链接http://www.cn-java.com/www1/bbs/viewthread.php?tid=93104&extra=page=1

参加会议:有人邀请A,B,C,D,E,F6个人参加一项会议,这6个人有些奇怪,因为他们有很多要求,已知:
(1).A,B两人至少有1人参加会议。
(2).A,E,F 3人中有2人参加会议。
(3).B和C两人一致决定,要么两人都去,要么两人都不去。
(4).A,D两人中只1人参加会议。
(5).C,D两人中也只要1人参加会议。
(6).如果D不去,那么E也决定不去。
那么最后究竟有哪几个人参加了会议呢?

题外话

最开始在上面的原文论坛回答一个小伙伴的提问,后来换了几家公司原代码已经找不到了,下面的代码是之后整理过的代码。今年2月份的时候在CSDN博客上看到被人收集并且标记为原创,感觉自己的东西被人偷去了一样难受,于是又整理了一份。


推理

从4,5两个条件出发A,D互斥同时C,D互斥(当然这里有一定歧义,只要1人参加可以理解为必须选1人参加或者理解为要么1人参加要么两个都不参加,因为这里没有强调必须去。)

  • 假设D参加,那么A,C不参加,根据条件(1)B就必须参加 得到BD
    再根据条件(3)要求BC要么同去要么同不去,所以C也必须去,得到BCD,与条件(5)相矛盾,所以D是肯定无缘参加了。
  • D不参加根据条件(6)得到E也不参加, 那么条件(2)中AEF就排除掉E 得到AF
  • 回到上面提的歧义,如果理解为后一种的话那AF可以满足所有条件, 如果理解为前一种的话D不参加C就必须参加 得到ACF, 再回到条件(3)可知B也必须去 最终得到ABCF
  • 那么最终的人选是ABCF 或者再算上歧义的结果AF

转换成程序的基本思路是找出6人所有可能的组合,然后根据1~6这6个条件一个一个判断是否满足,找出完全满足条件的组合即可。应该也有更好的思路不用进行组合,先假定6人都去然后再根据条件进行排除。

  1. 获取所有组合
    得到A,B,C,D,E,F,AB,AC,AD,AE,AF...ABCDEF这样的全组合,可以使用递归法(回溯),也可以使用一维数组。
    下面是使用一维数组的方法。
    /**
     * 组合不排列
     * @param items
     * @return 数组中所有可能的组合
     */
    public static String[] combination(String[] items) {
        int n = items.length, index = 0, _n = n;
        // 每个人都有两种状态,要么去,要么不去所以总共的组合就有2^6,
        // 但是全部不去这种组合没有意义要去掉
        String[] comb = new String[(1 << n) - 1];
        System.arraycopy(items, 0, comb, 0, n);
        while (++index < n) {
            int j = _n;
            for (int s = 0, len = n - index; s < len; s++) {
                for (int ss = (j - count(n - s - 1, index)); ss < j; ss++) {
                    comb[_n++] = comb[s] + comb[ss]; // 最好使用StringBuilder
                }
            }
        }
        return comb;
    }

    protected static int count(int p, int c) {
        if (p < c) return 0;
        if (p == c) return 1;
        if (c > p >> 1) c = p - c;
        int _p = 1, _c = 1;
        for (int i = p, size = (p - c); i > size; i--) _p *= i;
        for (int i = 2; i <= c; i++) _c *= i;
        return _p / _c;
    }
  1. 得到组合遍历每个组合判断是否全部满足6个条件
    最初版是这样子
boolean match(String str) {
        // (1).A,B两人至少有1人参加会议。
        if (!str.contains("A") && !str.contains("B")) {
            return false;
        }
        // (2).A,E,F3人中有2人参加会议。
        if (!((str.contains("A") && str.contains("E"))
                || (str.contains("A") && str.contains("F"))
                || (str.contains("E") && str.contains("F")))) {
            return false;
        }
        // (3).B和C两人一致决定,要么两人都去,要么两人都不去。
        if ((str.contains("B") && !str.contains("C"))
                || (!str.contains("B") && str.contains("C"))) {
            return false;
        }
        // (4).A,D两人中只1人参加会议。
        if (str.contains("A") && str.contains("D")) {
            return false;
        }
        // (5).C,D两人中也只要1人参加会议。mark: 这里并没有说C,D必须有一人参加
        if (str.contains("C") && str.contains("D")) {
            return false;
        }
        // (6).如果D不去,那么E也决定不去。
        if (!str.contains("D") && str.contains("E")) {
            return false;
        }
        return true;
    }

后来整理的时候发现这些判断有很多冗余,比如分支1,2,4都有str.contains("A"),就想办法把它们提出去只调有一次,判断也可以演化为逻辑运算,比如条件(1)A,B至少1人参加可以改为(a|b) == 1,于是就有了一个改版

    boolean match1(String str) {
        // 每个人两个信号量1:去,0:不去
        int a = str.indexOf('A') > -1 ? 1 : 0
                , b = str.indexOf('B') > -1 ? 1 : 0
                , c = str.indexOf('C') > -1 ? 1 : 0
                , d = str.indexOf('D') > -1 ? 1 : 0
                , e = str.indexOf('E') > -1 ? 1 : 0
                , f = str.indexOf('F') > -1 ? 1 : 0;
        // (1).A,B两人至少有1人参加会议。
        if ((a | b) == 0) {
            return false;
        }
        // (2).A,E,F3人中有2人参加会议。
        if ((a & e | a & f | e & f) == 0) {
            return false;
        }
        // (3).B和C两人一致决定,要么两人都去,要么两人都不去。
        if ((b ^ c) == 1) {
            return false;
        }
        // (4).A,D两人中只1人参加会议。
        if ((a & d) == 1) {
            return false;
        }
        // (5).C,D两人中也只要1人参加会议。
        if ((c & d) == 1) {
            return false;
        }
        // (6).如果D不去,那么E也决定不去。
        if ((~d & e) == 1) {
            return false;
        }

        return true;
    }

改为之后发现为何不将非判断改为与判断呢?这样就可以将6个if判断合并为1个判断,最终改为了下面的形式

 boolean match2(String str) {
        int a = str.indexOf('A') > -1 ? 1 : 0
                , b = str.indexOf('B') > -1 ? 1 : 0
                , c = str.indexOf('C') > -1 ? 1 : 0
                , d = str.indexOf('D') > -1 ? 1 : 0
                , e = str.indexOf('E') > -1 ? 1 : 0
                , f = str.indexOf('F') > -1 ? 1 : 0;
        return ((a | b) // (1).A,B两人至少有1人参加会议。
                & (a & e | a & f | e & f) // (2).A,E,F3人中有2人参加会议。
                & ~(b ^ c) // (3).B和C两人一致决定,要么两人都去,要么两人都不去。
                & ~(a & d) // (4).A,D两人中只1人参加会议。必须去一个就改为 & (a ^ d)
                & ~(c & d) // (5).C,D两人中也只要1人参加会议。必须去一个就改为 & (c ^ d)
                & ~(~d & e) // (6).如果D不去,那么E也决定不去。
        ) == 1;
    }

最后只需要一个main函数就可以运行了。

public static void main(String[] args) {
        // 得到6个人所有的组合
        String[] list = combination(new String[] {"A", "B", "C", "D", "E", "F"});
        // 判断每种组合是否满足所有的条件
        for (String str : list) {
            if (match2(str)) {
                System.out.println(str);
            }
        }
    }

当然,运行结果与上面推论的一样,



下一个目标是不拉出所有组合直接处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容