将现实问题转换为编程问题
将现实问题转换为编程问题需要转换思维,不过孰能生巧,见多了就自然懂如何做了,所以动起手来是决没错的。
- 1.猜名次问题
- 改进一:
- 改进二:
- 改进三:
- 2.猜凶手问题
- 总结:
1.猜名次问题
每个选手都说了两句话,而只有一句话是对的
如果把 两句话都看成两个判断句, 如果为真则结果为1 如果为假则结果为0
所有两个判断式相加 等于1则表示只有一个判断句是真另外一个是假比如A选手说对一般表示:(b= =2)+(a= =3)==1
把所有的可能性穷举出来,a可能是第1第2第……5,b可能是第1第2第……5,c可能是……,嵌套就可以了。
思路:
考虑到一共五个人,直接模拟推理有些太难,计算机最擅长的遍历此时就会派上用场,将每个人从第1到第5来一遍,则一共会产生5^5种可能性,这个只需要一个5层循环即可搞定。但是这样会导致一些不期望出现的结果出现,因为并没有查重,所以会出现两个人抢名次的情况,也就是两个人或者更多的人名次相同的情况,例如两个第二,三个第三这样的,所以即使满足了条件,也要查看一下五个人的名次是否重复,这个交给一个函数来执行,只要五个人名次并列,那就返回0,否则返回1即可。有了这个思路,就能完成以下代码。
#include <stdio.h>int checkData(int* p)
{int tmp[7] = { 0 }; //标记表,实际是哈希表的思路。一开始每个元素都是0//用来表示名次是否有人了,如果p[0](a选手)的名次是3,则tmp[3]=1,表示第3名已经有人了//如果p[1](b选手)的名次也是3,则进入if()语句tmp[3]==1,//直接返回0,这组成绩作废,再判断下组成绩。//直到没有重复的 才可以才能返回1int i;for (i = 0; i < 5; i++){if (tmp[p[i]]==1) //如果这个位置的标记已经是1,则代表重复,直接返回0。{return 0;}tmp[p[i]] = 1; //如果不是,则给这个位置标记为1。}return 1; //全部标记完毕也没有出现重复的情况,代表OK。
}int main()
{int p[5]; //0 1 2 3 4分别代表a b c d efor (p[0] = 1; p[0] <= 5; p[0]++){for (p[1] = 1; p[1] <= 5; p[1]++){for (p[2] = 1; p[2] <= 5; p[2]++){for (p[3] = 1; p[3] <= 5; p[3]++){for (p[4] = 1; p[4] <= 5; p[4]++) //五层循环遍历{//这里是五个人的描述,由于比较表达式只有0和1两个结果,如果要两个条件有且只有一个为真,则可以用比较表达式的值总和为1的方式直接判定。别忘了还要判定不能并列。if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一checkData(p) //不能并列||,如果checkData返回0,则整体都为假,如果返回1则为真){for (int i = 0; i < 5; i++){printf("%d ", p[i]);}putchar('\n');}}}}}}return 0;
}
有的人可能不太懂为什么要查重复,因为5^5把全部的可能性列举出来,每个人都可能会相同,而题目给的也不是很严谨,所有会导致出现相同名次的情况比如把查重函数拿走就会出现这个现象:
会出现抢名次的现象,所以我们需要进行查重。
查重后结果:
改进一:
检查是否重复的过程,我们是用一个数组来做的,实际每个标签只有0和1两种可能,
没必要一定要用数组做,可以考虑用一个位来做(哈希中的位图)
代码如下:
int checkData(int *p)
{char tmp = 0;int i;for (i = 0; i < 5; i++){tmp |= 1 << p[i]; //tmp每次或上一位1,p[i]如果是1~5都有,则1<<1到1<<5都或上的结果将会是00111110,如果有并列,则一定会至少却其中一个1,结果就不会是00111110,所以可以判断tmp最终的结果是不是这个数字来判断有没有重复。}return tmp == 0x3E;
}
改进二:
循环代码又长又难看,可以考虑改成递归:
void diveRank(int * p, int n)
{if(n >= 5) //此时的n是用来控制循环层数的。{if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一checkData(p)) //查重{for (int i = 0; i < 5; i++){printf("%d ", p[i]);}putchar('\n');}return;}for(p[n] = 1; p[n] <= 5; p[n]++){diveRank(p, n + 1); //通过递归模拟多层循环,每进一次递归相当于进了一层新的循环。}
}int main()
{int p[5];diveRank(p, 0);return 0;
}
改进三:
以上的方法只是让代码简单了点,但还是需要5 ^ 5次比较,而如果本来就是做1 ~ 5的排列组合的话只需要5!次比较,能极大的减少遍历所需的次数(复杂度由O(n^n)降低为O(n!)),那是不是可以用一个递归完成对1~5的全排列呢?当然是可以的,所以我们可以进一步优化遍历的方式,将遍历用的递归程序改成这样:
#include <stdio.h>void swapArgs(int * a, int * b) //交换函数
{int tmp;tmp = *a;*a = *b;*b = tmp;
}void diveRank(int * p, int n)
{if(n >= 5) //此时的n也是用来控制循环层数的。{if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三(p[4] == 4) + (p[0] == 1) == 1) //我第四,A第一//由于此时是执行的全排列,所以查重也省了。{for (int i = 0; i < 5; i++){printf("%d ", p[i]);}putchar('\n');}return;}int i;for(i = n; i < 5; i++) //这个递归方式就完成了对1~5的全排列,方法是从后向前不停的执行交换。可以参考改进二和原代码,将这个递归程序写回成循环后,可以更好的理解。{swapArgs(p + i, p + n);diveRank(p, n + 1);swapArgs(p + i, p + n);}
}int main()
{int p[5] = { 1, 2, 3, 4, 5 }; //当然由于是全排列,所以初值必须给好。diveRank(p, 0);return 0;
}
至此,遍历速度上达到了一个新的高度,这种遍历大大减少了遍历的次数,极大的提升了效率。
2.猜凶手问题
这个还是将简单的现实问题转换为编程问题,跟上面的题目一样,问题就是如何把这几个人说的话转换为编程
四个嫌疑犯,3个人说了真话,1个人说了假话,那把这四个人的话看成4个表达式(判断式),如果为真则结果为1,如果为
假结果为0,所以只要让4个表达式相加等于3就可以了。
思路:
这个题就是按照正常方式,假设凶手是a,b,c,d其中的一个,看是不是满足题目条件,如果满足就找出了凶手。
int main()
{char killer = 0;//分别假设凶手是a,b,c,d,看谁是凶手是符合三个人说真话,一个人//说假话for (killer = 'a'; killer <= 'd'; killer++){if (('a' != killer) + (killer == 'c') + (killer == 'd') + (killer != 'd')==3){printf("凶手是:%c", killer);}}return 0;
}
总结:
这种问题可能刚做不适应,不太好想,不过接触多了就知道怎么回事了,将所说的看成表达式,根据真为1,假为0,来解释
多刷题,就能接触更多的有趣的题类型。
将现实问题转换为编程问题相关推荐
- 编程难学?3点解答你的疑惑
很多编程新手 都会套用以前上学时的学习方法: 记语法.定义.常量-- 然而,这些方法在编程学习中 却完全不奏效 编程究竟难在哪? 有没有更有效的学习方法呢? 往下翻看,解锁答案� 1.难在我们从未接受 ...
- 编写五子棋程序时如何添加下棋时的音效_干货:如何提高编程能力
注意!!本文字数较多!都是干货! 很多初学者都会遇到各种各样的问题,比如下面这些类型的: 1.只会像高中一样跟着课程学习 2.怎么可以脱离课本和教学视频自己编写一个小项目? 3.停于理论,不知道如何实 ...
- [个人推荐]函数式编程另类指南[zz]
原文链接:Functional Programming For The Rest of Us 原文作者:Vyacheslav Akhmechet 翻译:lihaitao (电邮: lihaitao ...
- 一篇关于编程的文章,启发你的编程知识
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 本文章来自腾讯云 作者:Python知识大全 想要学习Python?有问题得不到第 ...
- 函数式编程另类指南 (zz)
程序员拖沓成性,每天到了办公室后,泡咖啡,检查邮箱,阅读 RSS feed,到技术站点查阅最新的文章,在编程论坛的相关版面浏览公共讨论,并一次次地刷新以免漏掉一条信息.然后是午饭,回来后盯了IDE没几 ...
- 计算机编程好难啊,揭秘|为什么我们觉得编程好难?
原标题:揭秘|为什么我们觉得编程好难? 编程真的好难呀,孩子真能学会吗? 家长常问的这句话翻译一下就是:我一个成人人都觉得编程难,小孩子恐怕更学不会吧. 今天就来聊聊,为什么我们会觉得编程难?是真的所 ...
- (转)函数式编程另类指南
函数式编程另类指南 原文链接:Functional Programming For The Rest of Us http://www.opengpu.org/bbs/viewthread.php?t ...
- 函数式编程另类指南 (转载)
原文链接:Functional Programming For The Rest of Us 原文作者:Vyacheslav Akhmechet 翻译:lihaitao (电邮: lihaitao在g ...
- 少儿编程教育好不好---《浅谈青少儿编程教育与计算思维》
前言 最近国家政策导向,导致很多少儿教育行业迎来了寒冬,使得不少企业都快支撑不下去了,这也是国家对于校外少儿教育行业的规范化必经之路.我们今天不讲政策和形势,谈谈我对于少儿编程教育的一些认识.毕竟也在 ...
最新文章
- opencv线性滤波(滤波与模糊的区别)
- python元素定位的八种方法_selenium webdriver基于Python系列之八种元素定位方法
- 【Fragment】管理机制
- TDD代码驱动测试基础
- ubuntu 下的ftp安装及root身份远程配置
- 详解:设计模式之-单例设计模式
- 触发器及其应用实验报告总结_双面喷绘材料的分类及其应用,超全总结!(建议收藏)...
- 毕业季,我的Linux求职之路
- shell实战之日志脱敏
- base,override,virtual
- Git教程_3 IDEA管理
- jQuery最新1.4 版本的十五个新特性
- Selenium和Firefox对应版本
- 一种适用于FDD+TDD基站天线阵列的多天线共存方式
- 【从零开始玩量化7】easyTrader: 自动化(程序化)交易利器
- 2022年「博客之星」参赛博主:(天寒雨落)在等您评价 ~{附实时总榜单排名}
- C语言-英文字母倒序转换
- 【Linux】ps -ef 和ps aux 有什么不同呢?
- 苹果手机对html的要求,原神iPhone 8能玩吗?苹果手机最低配置要求一览
- 神经网络epoch和batch的粗浅理解