超级测谎机。

注:本文100%原创!

一、问题描述:

在某个镇某个夜晚,发生一起谋杀案,警察通过排查确定杀人凶手是4个嫌疑中的一个,以下是四个人的说词

A说:不是我

B说:是C

C说:是D

D说:C在胡说

其中只有3个人说了真话,1个人说了假话,现在根据这些信息,写一个程序来确定哪个是凶手。

二、问题分析

可以直接写死程序,但为了更加好玩,把程序写活,就得换另外一种思维了。有用户手动输入说话内容,然后根据内容来分析结果,输出。这里采用预备存储法,即根据用户的输入,预先得出有多少种可能的结果,然后再从中判断,最后输出。

三、涉及知识点

排列组合,动态数组,C语言简单操作。

四、举例分析

A说:不是我

B说:是C

C说:是D

D说:C在胡说

假如:用某个人说话的内容用大小写字母表示。小写字母表示凶手不是自己,大写字母表示凶手就是自己,即a表示,A是不是凶手,A表示A是凶手。所以案例输入应该为:

A说:a

B说:C

C说:D

D说:d

根据排列组合知识,得知:4个人,如果有3个人说真话,即从4个人中选择3个人出来(说真话的),或者从4个人中选择1个人出来(说假话),这里以说真话为例。所以为C(4,3)=4。

所以说话真假的矩阵也可以是:

ABCd  (d说假话)

ABcD(c说假话)

AbCD(b说假话)

aBCD(a说假话)

我们定义一个result数组,用来判断最终结果。如果我们这样,如果某个人的说话内容是A,那么对应result[0]就+1,0对应A,1对应b......, 如果某个人的说话内容是a,那么result[1]+1, result[2]+1, reuslt[3]+1, 即result[0]不加,最终根据result[i]的数值大小来判断,如果result[i]等于总人数,那么第i号人就是凶手,可能有多种情况,然后转换成判断矩阵来计算。

五、运行效果图

案例测试:手动计算只有一种结果

超级测谎机。_第1张图片

换一种测试:说话内容不变,说真话人数变为2个,手动计算知道有两种结果

超级测谎机。_第2张图片

再换一种测试,7个人,5个说真话的,

超级测谎机。_第3张图片

其他的不演示了,因为人数越多越不好验证 ,数学系的同学深有体会!

下面附上代码,其中有包含了排列组合的代码

六、代码

C语言代码如下:

#include

#include

int cNumber(int n, int m);

int combine(int n, int m, char *tell);

char correct(char arr, char tell);

int check(int n, char *tell, char *people, int *result);

int main(void)

{

    int m, n;

    printf("请输入测试者人数:");

    scanf("%d", &n);

    printf("请输入说真话的人数:");

    scanf("%d", &m);

    char *tell = calloc(n, sizeof(char));

    printf("请输入说话内容(假如X是凶手,请输入X, 否则请输入x)\n");

    for(int i=0; i

    {

        printf("%c说的话:", 'A'+i);

        getchar();      //清空缓冲区

        scanf("%c", &tell[i]);

    }

    printf("\n测谎开始!\n\n");

    combine(n, m, tell);

    return 0;

}

//计算组合数种类

int cNumber(int n, int m)

{

    int i, tmp=1, group=1;

    for(i=2; i<=m; i++)

    {

        tmp *= i;

    }

    for(i=0; i

    {

        group *= (n-i);

    }

    return group/tmp;

}

//纠正原话的结果

char correct(char show, char tell)

{

    if(show>='A' && show<='Z')    //说真话

    {

        return tell;              //返回原话

    }

    else if(tell>='A' && tell<='Z')

    {

        return tell+'a'-'A';      //返回原话的反话

    }

    else

    {

        return tell-('a'-'A');    //返回原话的反话

    }

}

//检测出结果

int check(int n, char *tell, char *people, int *result)

{

    int i, j, k;

   

    for(i=0; i

    {

        result[i] = 0;

    }

    for(i=0; i

    {

        for(j=0; j

        {

            if(tell[i] == people[j])       //直接说是people[j]

            {

                result[j]++;               //people[j]怀疑性加一

                break;

            }

            else if(tell[i]>='a' && tell[i]<='z' && (tell[i]- ('a'-'A')) == people[j])    //说不是people[j]

            {

                for(k=0; k

                {

                    if(k!=j)

                    {

                        result[k]++;        //除了people[i]外的人怀疑性加一

                    }

                }

                break;

            }

        }

    }

    //当且仅当只有一个result[i]==n时,检测结果合理

    for(i=0, j=0 ; i

    {

        if(result[i]==n)

        {

            k=i;       //记录凶手编号

            j++;       //假如检测出有多个凶手

        }

      //printf("result[%d]=%d, ", i, result[i]);

    }

   

    if(j==1)

    {

        printf("\n测试结果: 凶手是%c!\n", people[k]);

        return 1;

    }

   

    return 0;

}

int combine(int n, int m, char *tell)

{

    int i, j, k, count, group;

    group = cNumber(n, m);

    char **show = calloc(group, sizeof(char*));   //用来存储所有可能情况

    for(i=0; i

    {

        show[i] = calloc(n, sizeof(char));

    }

    for(i=0; i

    {

        for(j=0; j

        {

            show[i][j] = 'a'+ j;

        }

    }

    if(n

    {

        printf("输入组合数非法!\n");

        return -1;

    }

    char *people = calloc(n, sizeof(char));   //用来存储有多少个人

    for(int i=0; i

    {

        people[i] = i+'A';

    }

    int *a = calloc(m, sizeof(int));  //用来存储每次产生的一种组合

    for(i=0; i

    {

        a[i] = i+1;

    }

    for(j=m, count=0; a[0]<=(n-m+1);)  //当为最后一种组合时,循环结束

    {

        for(;a[m-1]<=n; a[m-1]++)  //最后一位不断递增,直到达到最大值,产生进位

        {

            count++;

            //printf("第%d种组合: ", count);    //计算组合数种类

            for(k=0; k

            {

                //printf("%c", arr[a[k]-1]);

                show[count-1][people[a[k]-1]-'A'] = people[a[k]-1];

            }

            //printf("\n");  

        }

        for(j=m-2; j>=0; j--)  //判断a[1]--a[m-2]是否有进位

        {

            a[j]++;

            if(a[j] <= (j+n-m+1))  //a[j]不进位,a[j-1]也不进位

            {

                break;

            }

        }

        for(j++; j>0 && j

        {

            a[j] = a[j-1] + 1;

        }

    }

    int *result = calloc(n, sizeof(int));

    char *correct_tell = calloc(n, sizeof(char));

    //计算所有组合情况对应的结果

    for(i=0; i

    {

        for(j=0; j

        {

            correct_tell[j] = correct(show[i][j], tell[j]);

            //printf("correct_tell[%d]=%c ", j, correct_tell[j]);

        }

        k = check(n, correct_tell, people, result);

        if(k==1)

        {

            for(k=0; k

            {

                //printf("show[%d][%d]=%c  ", i, k , show[i][k]);

                if(show[i][k]>='A' && show[i][k]<='Z')

                {

                    printf("%c说真话", people[k]);

                }

                else

                {

                    printf("%c说假话", people[k]);

                }

                printf("\n");

            }

        }

        //printf("\n");

    }

    //释放堆内存

    for(i=0; i

    {

        free(show[i]);

    }

    free(show);

    free(people);

    free(result);

    free(correct_tell);

    free(a);

    getchar();

    return 0;

}

当然本次的测谎机有一点的局限性,人数最多26个,很显然的,当然可以修改;用户需要判断说话内容来输入;没有对用户的输入做太多合法性判断;当无法确定凶手时,无法输出,只能判断只有一个凶手的情况,不能判断出团伙作案的情况。

感兴趣的小伙伴可以点个关注哦,下次改造成语音识别的,做个真正的测谎机!

你可能感兴趣的