标题:A、B、C、D、E、F、G、H、I、J 共10名学生有可能参加本次计算机竞赛,也可能 ...
只看楼主
虾B写
Rank: 8Rank: 8
来 自:湖北
等 级:蝙蝠侠
威 望:3
帖 子:395
专家分:922
注 册:2009-10-1
得分:0 
只看前两个判断去写会变的简单,找出共同点就行了


1. 如果A参加,B也参加;
   2. 如果C不参加,D也不参加;



      1[1]ABCDEFGHIJ  1[2]CDEFGHIJ
      2[1]ABFGHIJ 2[2]ABCDEFGHIJ
      1[1]与2[1]的交集  1[1]与2[2]的交集 .........共4个集合
      写个函数   F(N1[],N2[]) 返回一个数组,length = N1.length * N2.length


接下来就不用想了。
3. A和C中只能有一个人参加;
   3[1]ABDEFGHIJ 3[2]BCDEFGHIJ
F(之前返回的数组,N3)  返回长度为六的新数组
。。。。。。。。。。
记得去除空集就行了。


只是想想,不知道可行不










白娘故意下雨骗许仙的伞。祝英台十八里相送时装疯卖傻调戏梁山伯。七仙女挡住了董永的去路。牛郎趁织女洗澡时拿走了她的衣服。。。这些故事告诉我们;伟大爱情的开始,总归的有一个要先耍流氓!
2011-06-04 10:34
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:0 
这是一个典型的命题逻辑推理问题,相关知识各位找本离散数学看吧,我就不详述了。2、3楼两位觉得无从下手,那么我就从如何下手说起吧。

在解决任何一个问题,尤其是用计算机解决时,之前,先要分析问题。而要想借助某种手段分析问题的前提是,先用某种方式描述清楚问题。也可以说是将问题符号化。再换一种说法就是对问题建模。

上面的话可能有点抽象,但应该不难理解。两位无从下手的原因就在于不知道如何用代码表述这个问题。而表述问题的这种能力就是衡量一个编程者水平的指标,也是我们进行软件学习的主要目的。要提高这一能力只能靠我们不断的学习与练习。

形而上的的东西说完了,现在看看上面那几句云里雾里的东西是如何具体落实在这个问题上的。

问题中有10个人,分别用字母A-J表示(这就是一个符号化,只是这是具象到纸面的符号化)。这些人在满足一系列条件下有些人会参加比赛,另一些人会不参加比赛。

这意味着,每个人在一个结果中有两种可能的状态,参加或不参加。10个人的状态构成一组结果。由此可知可能的结果共有2的10次方组即1024组。这是问题的解空间。在计算机中表示一组结果的方法有很多种。用1表示参加,用0表示不参加,用一个布尔型或整型或你喜欢的其它型变量表示一个人,用一个数组表示一个结果等等等等。

在我的代码中用1个二进制位表示一个人,从低位到高位分别表示A-J。共用了10位。这样一组结果只需要用一个大于10位的量来表示就可以了。我用的是int型。我的编译器是GCC,它是32位的。在TC里int是16位的。

到这里,问题已经被表述完整了。接下来就是在这一表述下的运算。我的代码就是对解空间中的每一个解验证一遍那一组条件,然后将满足所有条件的解输出。关于如何验证条件满足与否,我就以第一个条件为例说明一下。

Test函数的第1个循环用于将由整数表述的解转换成由数组表述的解。整数表述占用的空间小,便于传递,但做条件判断不方便。数组表述易于运算,但浪费的空间多。所以我在不同的阶段使用不同的表述方式。

条件1:如果A参加,B也参加;
这是一个蕴含式描述。即A->B,等价于!A || B。即是说对于一组结果,如果其中A为1,且B也为1;或者A为0,那么这组结果满足该条件。
Test函数中的第1个if语句就是描述这一条件的。由于各条件之间是与关系,即任意一个条件不成立,解就是不成立的。所以我将if语句级联着写。其实它们是嵌套关系,只不过我省略了花括号和缩进,而且这样写我觉得更美观也易于理解。

为了方便大家讨论,再复制一次代码到这里。
程序代码:
#include <stdio.h>

void Test(int comb)
{
    int a[10], i;
   
    for(i = 0; i < 10; a[i] = (comb >> i++) & 1);
   
    if(!a[0] || a[1]) /*1. 如果A参加,B也参加;*/
    if(a[2] || !a[3]) /*2. 如果C不参加,D也不参加;*/
    if(!a[0] || !a[2]) /*3. A和C中只能有一个人参加;*/
    if(a[1] && !a[3] || !a[1] && a[3]) /*4. B和D中有且仅有一个人参加;*/
    if(!(a[3] && !a[4] && !a[5] && !a[6] && !a[7] ||
        !a[3] && a[4] && !a[5] && !a[6] && !a[7] ||
        !a[3] && !a[4] && a[5] && !a[6] && !a[7] ||
        !a[3] && !a[4] && !a[5] && a[6] && !a[7] ||
        !a[3] && !a[4] && !a[5] && !a[6] && a[7] ||
        !a[3] && !a[4] && !a[5] && !a[6] && !a[7])) /*5. D、E、F、G、H 中至少有2人参加;*/
    if(a[2] && a[6] || !a[2] && !a[6]) /*6. C和G或者都参加,或者都不参加;*/
    if(!(!a[2] && a[4] && a[6] && a[8] ||
        a[2] && !a[4] && a[6] && a[8] ||
        a[2] && a[4] && !a[6] && a[8] ||
        a[2] && a[4] && a[6] && !a[8] ||
        a[2] && a[4] && a[6] && a[8])) /*7. C、E、G、I中至多只能2人参加;*/
    if(!a[4] || a[5] && a[6]) /*8. 如果E参加,那么F和G也都参加;*/
    if(!a[5] || !a[6] && !a[7]) /*9. 如果F参加,G、H就不能参加;*/
    if(a[8] || a[9] || a[7]) /*10. 如果I、J都不参加,H必须参加。*/
    {
        for(i = 0; i < 10; i++)
            if(a[i]) printf("%c ", 'A' + i);
        printf("\n");
    }
}

int main()
{
    int i;
    for(i = 1; i <= 0x3FF; Test(i++));
    return 0;
}


8楼这位提出一个交集的思想,不过在11楼的描述我实在是没看懂。编程的乐趣就在于想,进而写出来获得成功的快感。如果嫌累,那我觉得可能不适合走这一条路。现在做什么事情不累呢?话又说回来,搞软件如果很轻松那这个行业又凭什么拿那么高的薪水呢?

关于交集谈谈我的理解。
上面的代码是对每一个解做一系列的条件判断,然后确认该解是成立还是不成立。
换个角度,这个问题还可以这样解决,用一个条件过滤解空间,得到关于该条件成立的解集(解空间收缩)。用下一个条件再过滤这个解集,得到新的既满足之前条件又满足现在条件的解集。当所有的条件都过滤完后,剩下的解集就是问题要求的结果。

听起来不错。分析一下算法的时间复杂度:
设人数为N,条件数为M;
算法1的复杂度为 O(2^N * M);
算法2的复杂度为 O(2^N + 2^(N-1) + 2^(N-2) + ... + 2^(N-M))=》 O(2^N);
算法2的复杂度公式是一个宏观统计公式,认为每次过滤可以滤去一半的解。

由上面的分析可知,当条件的数量不变时,两个算法的时间复杂度是相同的。当条件的数量为变量时,算法2优于算法1。
总的来说,2的N次方这样的复杂度不可能解大规模的命题的。对于这个问题,如果人数扩大到30人,想找到所有解将是很困难的。但找出几个解还是可以的。
下面是我按照算法2的思路完成的代码,希望大家指正。
程序代码:
#include <stdio.h>

typedef char BOOL;

typedef struct node
{
    char a[10];
    struct node * next;
}NODE;

NODE ns[1024];

void Initial()
{
    int i, j;
    for(i = 0; i < 1024; i++)
    {
        for(j = 0; j < 10; ns[i].a[j] = (i >> j++) & 1);
        ns[i].next = (i == 1023) ? NULL : &ns[i + 1];
    }
}

BOOL T1(char *a) { return !a[0] || a[1]; }
BOOL T2(char *a) { return a[2] || !a[3]; }
BOOL T3(char *a) { return !a[0] || !a[2]; }
BOOL T4(char *a) { return a[1] && !a[3] || !a[1] && a[3]; }
BOOL T5(char *a) { return !(a[3] && !a[4] && !a[5] && !a[6] && !a[7] ||
                            !a[3] && a[4] && !a[5] && !a[6] && !a[7] ||
                            !a[3] && !a[4] && a[5] && !a[6] && !a[7] ||
                            !a[3] && !a[4] && !a[5] && a[6] && !a[7] ||
                            !a[3] && !a[4] && !a[5] && !a[6] && a[7] ||
                            !a[3] && !a[4] && !a[5] && !a[6] && !a[7]); }
BOOL T6(char *a) { return a[2] && a[6] || !a[2] && !a[6]; }
BOOL T7(char *a) { return !(!a[2] && a[4] && a[6] && a[8] ||
                            a[2] && !a[4] && a[6] && a[8] ||
                            a[2] && a[4] && !a[6] && a[8] ||
                            a[2] && a[4] && a[6] && !a[8] ||
                            a[2] && a[4] && a[6] && a[8]); }
BOOL T8(char *a) { return !a[4] || a[5] && a[6]; }
BOOL T9(char *a) { return !a[5] || !a[6] && !a[7]; }
BOOL T10(char *a) { return a[8] || a[9] || a[7]; }

BOOL (*condition[])(char *) = { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 };

void Test()
{
    int i;
    NODE *head, *p, *q;
    head = ns;
    for(i = 0; i < 10; i++)
    {
        p = head;
        q = p->next;
        while(q)
        {
            if(condition[i](q->a)) p = q; else p->next = q->next;
            q = q->next;
        }
    }
}

void Output()
{
    int i;
    NODE * p;
    p = ns[0].next;
    while(p)
    {
        for(i = 0; i < 10; i++)
            if(p->a[i]) printf("%c ", 'A' + i);
        printf("\n");
        p = p->next;
    }
}

int main()
{
    Initial();
    Test();
    Output();
    return 0;
}



重剑无锋,大巧不工
2011-06-04 14:55
虾B写
Rank: 8Rank: 8
来 自:湖北
等 级:蝙蝠侠
威 望:3
帖 子:395
专家分:922
注 册:2009-10-1
得分:0 
别说扩大到30人,你扩大到3W人,你可以统计不在的人的集合,好像要用并集,得到最终结果后取反就行了。
哈哈哈,我懒是因为我不是闲人,看过十全九美不,每个人都说自己很忙。


白娘故意下雨骗许仙的伞。祝英台十八里相送时装疯卖傻调戏梁山伯。七仙女挡住了董永的去路。牛郎趁织女洗澡时拿走了她的衣服。。。这些故事告诉我们;伟大爱情的开始,总归的有一个要先耍流氓!
2011-06-04 15:44
hjywyj
Rank: 11Rank: 11Rank: 11Rank: 11
等 级:小飞侠
威 望:3
帖 子:1114
专家分:2611
注 册:2010-4-14
得分:2 
这个题逻辑是不是有问题啊?
 8. 如果E参加,那么F和G也都参加。//e参加,则f参加,g也参加
   9. 如果F参加,G、H就不能参加  //f已经参加了,g不能参加

故而如果条件成立,则e就不能参加
2011-06-04 15:46
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:0 
回复14楼:
你的分析是正确的,但这并不表示逻辑有问题。最总的结果即使一个人也没有,那也只能说明这10个人以这样的条件下不能参赛。
事实上,AEFI都不能参加。

回复13楼:
呵呵,这件事情比较矛盾。单看你的两个贴子实在不知道你在说些什么。想看看代码,你又没时间写。咱们也别打嘴仗,你还是抽点时间写个代码交流的好。
我的两段代码从构思到调试加起来也就半个小时的时间,倒是发贴造句花了我一个多小时。

重剑无锋,大巧不工
2011-06-04 16:16
键盘农夫
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:88
专家分:106
注 册:2011-5-5
得分:2 
以下是引用beyondyf在2011-6-3 23:40:25的发言:

呵呵,怎么没人讨论了?我的代码有没有什么问题?哪位有不同见解?
把那些判断写成宏,可读性会更好

《狂人C:程序员入门必备》
2011-06-04 18:21
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
得分:0 
以下是引用键盘农夫在2011-6-4 18:21:57的发言:

把那些判断写成宏,可读性会更好

这个提议不错,用参数宏可以提高可读性,也易于修改,哪位愿意帮忙改?

重剑无锋,大巧不工
2011-06-04 18:46
llq17501
Rank: 1
等 级:新手上路
帖 子:1
专家分:2
注 册:2011-6-7
得分:2 
if(a[3] + a[4] + a[5] +a[6] +a[7] >=2) /*5. D、E、F、G、H 中至少有2人参加;*/
    if(a[2] + a[4]+ a[6] +a[8]<=2) /*7. C、E、G、I中至多只能2人参加;*/
2011-06-07 16:08
voidx
Rank: 12Rank: 12Rank: 12
来 自:邯郸
等 级:火箭侠
帖 子:1250
专家分:3538
注 册:2011-4-7
得分:2 
回复 4楼 beyondyf
依然暴力
2011-06-07 18:07
jasoni
Rank: 1
等 级:新手上路
帖 子:2
专家分:0
注 册:2012-3-27
得分:0 
我这里有一个比较好的源代码(非原创)这是我在竞赛视频里看到的,然后自己手打出来的(已调试正确运行)。
此代码没有使用太多累赘的逻辑运算,并且在curision函数中使用了递归的方法,看起来简洁清爽。
另外需要注意的是他把蕴涵词(“如果那么运算转化成了或运算”)—— ( a-->b ) 等价于 ( ~a||b ) ,
此公式详见离散数学教材。
程序代码:
#include <stdio.h>

void show(int *x);
int judege(int *x);
void curision(int *x, int n);

int main()
{
    int x[10];
    curision(x,0);
    return 0;
}

void show(int *x)
{
    for(int i=0; i<10; i++)
        if(x[i]>0) printf("%c ", i+'A');
        putchar(10);
}

int judge(int *x)
{
    int t1= x[0]==0 || x[1]==1;
    int t2= x[2]==1 || x[3]==0;
    int t3= x[0] + x[2] <= 1;
    int t4= x[1] + x[3] == 1;
    int t5= x[3] + x[4] + x[5] + x[6] + x[7] >=2;    
    int t6= (x[2]+x[6]==2) || (x[2]+x[6]==0);
    int t7= x[2]+x[4]+x[6]+x[8] <= 2;
    int t8= x[4]==0 || (x[5] + x[6]==2);
    int t9= x[5]==0 || (x[6] + x[7]==0);
    int t10= (x[8]+x[9]>0) || x[7]==1;

    return  t1 && t2 && t3 && t3 && t4 && t5 && t6 && t7 && t8 && t9 && t10;
}

void curision(int *x, int n)
{
    if(n>=10)
    {
        if(judge(x)) show(x);
        return;
    }

    x[n]=0;
    curision(x,n+1);
    x[n]=1;
    curision(x,n+1);
}
2012-03-31 11:31



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-341841-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.430655 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved