编程之美-第1章 游戏之乐
1. 使CPU占有率画出正弦曲线
linux下的代码:
#if 0 /** Q1.1*/ int get_tick_count(){struct timeval tv;gettimeofday(&tv,NULL);return tv.tv_sec*1000000+tv.tv_usec; } const int Interval=300; const int COUNT=300; const double split=0.01; const double PI=3.1415926;int main(){int busySpan[COUNT];int idleSpan[COUNT];int half=Interval/2;int radian=0;for(int i=0;i<COUNT;i++){busySpan[i]=(int)(half+sin(PI*radian)*half);idleSpan[i]=Interval-busySpan[i];}int j=0;int start;while(true){j=j%COUNT;start=get_tick_count();while(get_tick_count()-start<=busySpan[j]);sleep(idleSpan[j]);j++;} }#endif
2. 中国象棋将帅问题
有三种方法:
方法1:
使用一个字节, 高4位表示A的位置,然后低4位表示B的位置, 然后剩下的就是分别对高4位和低4位进行操作了.
代码如下, 对位操作都是用宏实现的, 这样就不会占用其他变量了.
#if 0 /** Q1.2*/ #include <iostream> #define OFFSET 4 //#define FMASK 0xFF #define LMASK 0xF0 #define RMASK 0x0F #define LGET(b) ((LMASK&b)>>OFFSET) #define RGET(b) (RMASK&b) #define RSET(b,n) (b=((LMASK&b)|n)) #define LSET(b,n) (b=((RMASK&b)|n<<OFFSET)) int main(){unsigned char b;int i=0;for(LSET(b,1);LGET(b)<=9;LSET(b,LGET(b)+1)){for(RSET(b,1);RGET(b)<=9;RSET(b,RGET(b)+1)){if(LGET(b)%3==RGET(b)%3)continue;cout<<"A:"<<LGET(b)<<' '<<"B:"<<RGET(b)<<endl;i++;}}cout<<i<<endl; } #endif
方法2:
使用一个char类型变量, 由于A和B的位置个数都是9个, 所以我们将”AB”想象成一个9进制的二位数, 那么该9进制数最小为0, 最大为80, 我们的工作就是取出该9进制的高位和低位, 然后判断高位和低位是否符合要求.
代码如下:
#if 0 /** Q1.2*/ int main(){char b=81;int i=0;while(b--){if(b/9%3==b%9%3)continue;cout<<"A:"<<b/9<<' '<<"B:"<<b%9<<endl;i++;}cout<<i<<endl; }#endif
方法3:
方法3和方法1类似, 只是采用了另一种的思考方式, 使用了C/C++的struct的一个特殊用法:位域.
代码如下:
#if 0 /** Q1.2*/ struct _p{unsigned char a:4;unsigned char b:4; }p; int main(){int i=0;for(p.a=1;p.a<=9;p.a++)for(p.b=1;p.b<=9;p.b++) {if(p.a%3==p.b%3)continue;cout<<"A:"<<p.a%3<<' '<<"B:"<<p.b%3<<endl;i++;}cout<<i<<endl; }#endif
3. 烙饼排序
基本思路:
4. 买书问题
分析:
这个题先考虑贪心算法, 优先考虑最大的折扣, 但是我们可以发现贪心策略会失效的:
再考虑动态规划的方法:
为了更好的使用动态规划的方法, 我们要考虑一下如何表示所出现的中间状态.
我们使用Xi表示第i(1<=i<=5) 卷购买数量. F(X1,X2,X3,X4,X5)表示所需要的费用. 从题目的要求中我们可以看出, Xi之间的大小关系是无关紧要的, 因为打折的方法只是要求卷不同, 与具体是哪一卷无关. 为了更好的表示, 我们利用(Y1,Y2,Y3,Y4,Y5)且Y1>=Y2>=Y3>=Y4>=Y5来表示(X1,X2,X3,X4,X5)的所有排列.
这样可以得到状态转移方程:
5. 快速找出故障机器
这儿给出了4种解法:
方法1:
最直接的方法就是对这个ID列表进行遍历, 同时记录每个ID出现的次数, 根据最后记录得到的次数很容易找到出现一次的ID. 不论有几台机器死机, 这种情况都可以完成. 这种情况的时间复杂度是O(N), 空间复杂度是O(N)
方法2:
由于在这个问题中, 大部分ID出现的次数都是2, 所以这个2我们可以不必存储.
做法就是: 使用一个变长数组记录ID出现的次数, 如果某个ID出现次数等于2, 则删除这个ID, 则最后剩下的ID就是出现一次的ID. 这个方法最好情况下空间为O(1), 最坏为O(N)
方法3:
这个方法的思想是寻找一个函数, 该函数利用所有的ID可以计算出缺失的ID. 这样的一个函数比较典型的是异或运算, 由于异或运算满足结合律和交换律, 所以将所有ID进行异或之后, 如果ID出现两次, 结果就为0, 这样最终的结果就是只出现一次的ID.
如果只有一台机器死机, 那么该ID就是所求的ID. 如果有两台机器死机, 比如A和B, 如果A=B那么A⊕B=0, 就是说一份数据的两个拷贝都丢失了, 这样我们就没法确认是哪个了. 如果A⊕B不为0, 那么其结果中肯定有一位为1,这样就根据该位是否为1将所有的ID分为两类:一类是该位为1, 一类是该位为0, 这样两类ID分别异或就得到了A和B.
方法4:
该方法是寻找所有ID的一个不变量, 这个不变量就是所有ID的和.所以可以预先计算出所有ID的和,然后再计算出当前所有ID和, 两者的差就是出现一次的ID. 如果是有两个ID出现一次的话,则x+y=a. 这样为了求出x和y, 我们还必须找出一个方程. 可以用同样的方式计算x*y=b, 或者x^2+y^2=c,这样都可以求出x和y.
1.6 饮料供货
1.7 光影切割问题
1.8 电梯调度算法
代码如下:
void best_floor(int *presons,int n,int &floor,int& min_floors){if(persons==0||n<=0)return;floor=-1;min_floors=0x7fffffff;int count=0;for(int i=0;i<n;i++){for(int j=1;j<i;j++)count+=persons[j]*(i-j);for(int j=i+1;j<n;j++)count+=persons[j]*(j-i);if(floor==-1||min_floors>count){min_floors=count;floor=i;}} }
代码如下:
void best_floor2(int *presons,int n,int &floor,int& min_floors){if(persons==0||n<=0)return;int N1=0,N2=persons[0],N3=0;min_floors=0;for(int i=1;i<n;i++){N3+=persons[i];min_floors+=persons[i]*i;}for(int i=1;i<n;i++){if(N1+N2<N3){floor=i;min_floors+=(N1+N2-N3);N1+=N2;N2=persons[i];N3-=persons[i];}elsebreak;} }
1.9 高效的安排见面会
1.11 NIM(1) 一排石头的游戏
1.12 NIM(2)
问题:
有N块石头和两个玩家A和B,玩家A先将石头随机分成若干堆,然后按照BABA...的顺序不断轮流取石头,能将剩下的石头一次取光的玩家获胜,每次取石头时,每个玩家只能从若干堆石头中任选一堆,取这一堆石头中任意数目(大于0)个石头。
请问:
玩家A要怎样分配和取石头才能保证自己有把握取胜?
如果石头的个数N为偶数,A只要将其分为相同的两份,就一定能取胜。
初始:XOR(M1, M1) == 0
玩家B:XOR(M1, M2) != 0 (其中一堆的个数减少到M2)
玩家A:XOR(M2, M2) == 0 (玩家A将另一堆的个数也减少到M2)
结果:XOR(M2, M2) == 0 (直到结束状态(0, 0))
如果石头的个数N为奇数,B有必胜的方法。
初始:XOR(M1, M2, ... , Mn) != 0
玩家B:XOR(M1, ... , Mi', ... , Mn) == 0 (其中一堆Mi的个数减少到Mi')
玩家A:XOR(M1, ... , Mj', ... , Mn) != 0
玩家B:XOR(M1, ... , Mi', ... , Mn) == 0 (其中一堆Mi的个数减少到Mi')
结果:XOR(M1, ... , Mj' , ... , Mn) == 0 (直到结束状态(0,0))
这里就有个问题:已知XOR(M1, M2, ... , Mn) != 0,玩家B该改变那个Mi以使得XOR(M1, ... , Mi', ... , Mn) == 0呢?
对于这个问题的答案,书中并未准确的结论。
经过本人的分析,所得到的结论如下:
设k=XOR(M1, M2, ... , Mn),已知k!=0,取一个数Mi,其二进制表达中在k的最高二进制位上的数为1,且这个
数Mi肯定存在(k的这个最高位在异或运算中肯定来自某一个Mi)。在程序中满足(Mi&k) > (k>>1)条件的数即为Mi。
简单证明:即假设k的二进制表达是1xx,那么Mi的二进制表达是x...x1xx,这样玩家B将该Mi改成Xi'=XOR(Mi, k)后,
Mi'的二进制表达是x...x0yy,肯定小于Mi,并且有XOR(M1, ... , Mi', ... , Mn) == 0。
参考: http://blog.csdn.net/linyunzju/article/details/7661060
1.13 NIM(3) 游戏
问题:
假设有两堆石头,有两个玩家会根据如下的规则轮流取石头:
每人每次可以从两堆石头中各取出数量相等的石头,或者仅从一堆石头中取出
任意数量的石头;最后把剩下的石头一次拿光的人获胜。请问在哪些局面(依
据两堆石头中的石头个数)下,先取石头的玩家有必胜的策略。
解法:
类似构造质数的筛选方法,这里我们利用找到的必输局面(后取的玩家有必胜策略)
来筛去掉能通过一次操作达该必输局面的其它必胜局面(先取的玩家有必胜策略)。
最后选出的局面都是必输局面。
构造必胜策略:
如果一开始的局面就是必输局面,那么可能先取的玩家没有必胜策略(当然如果后取
的玩家不太聪明,先取的玩家依然有可能能赢)。如果一开始的局面不是必输局面,
那么先取的玩家一定有必胜策略,且必胜策略就是保证每次都将当前非必输局面转变
为必输局面(后取的玩家必输)。
参考:http://blog.csdn.net/linyunzju/article/details/7674596
1.15 构造数独
问题:
构造一个9*9的方格矩阵,玩家要在每个方格中,分别填上1至9的任意一个数字,
让整个棋盘每一列、每一行以及每一个3*3的小矩阵中的数字都不重复。
首先我们通过一个深度优先搜索来生成一个可行解,然后随机删除一定数量的数字,
以生成一个数独。
参考: http://blog.csdn.net/linyunzju/article/details/7673959
1.16 24点游戏
问题:
给玩家4张牌,每张牌的面值在1-13之间,允许其中有数值相同的牌,采用加、减、乘、除四则运算,允许中间运算存在小数,并且可以使用括号,但每张牌只能用一次。构造表达式,使其结果为24.
解法:
传统的枚举解法会产生大量重复的运算,主要有两类重复:运算结果的重复和排列的重复。假设4张牌为3 3 8 8,我们对3 3进行一次操作(6种运算)得到6 0 0 1 1 9,其中重复的数据就是我们所说的运算结果重复,使用集合不重复性来解决。枚举算法在枚举时要对牌的顺序进行排列,由于牌可以重复,所以产生的排列会有大量的重复(3 3) 8 8, (3 8) 3 8, (3 8) 3 8, (3 8) 3 8,(3 8) 3 8, (8 8) 3 3,这属于排列重复,使用分治法加memo来解决。采用二进制数来表达集合和子集的概念,我们可以用一个数来表示子集中拥有哪些元素,再用这个数作为索引来找出该集合运算后产生的结果集。
1.17 俄罗斯方块
问题:
让电脑自动下俄罗斯方块游戏。
解法:
对当前的积木块,枚举它旋转后的每一个形状从每一列落下的棋盘,将该棋盘和前一个棋盘进行对比,并打分,最后取得分最高的那个形状和那一列作为电脑的当前操作。(由于程序的输入数据比较多,我将其和代码打包放在资源下载中,需要的读者可以去下载http://download.csdn.net/detail/linyunzju/4389102,鉴于有读者不会用这个程序,在此说明下,运行这个程序需要加上文件重定向<1_17.txt,或者把程序中的控制台输入scanf改成文件输入FILE* fin=fopen("1_17.txt"); fscanf(fin,"%d",...);)
代码:
参考:
http://blog.csdn.net/linyunzju/article/details/7686822
转载于:https://www.cnblogs.com/xkfz007/archive/2012/11/07/2758267.html
编程之美-第1章 游戏之乐相关推荐
- 第1章 游戏之乐——连连看游戏设计
连连看游戏设计 连连看是一种很受大家欢迎的小游戏.微软亚洲研究院的实习生们就曾经开发过一个类似的游戏--Microsoft Link-up. 图1-17为Microsoft Link-up的一个 ...
- 编程之美中的NIM游戏及异或性质应用
最近看<编程之美>一书,感叹思维之妙,不过看过之后又在很多数学方面的书里面找到了同样的解法例如<组合数学>.<图论导引>等,之后才知道其实很多书上的算法都是源自数学 ...
- 第1章 游戏之乐——构造数独
构造数独 1. 问题 构造一个9*9的方格矩阵,玩家要在每个方格中,分别填上1至9的任意一个数字,让整个棋盘每一列.每一行以及每一个3*3的小矩阵中的数字都不重复. 2. 求解 用转置的方法生成数独数 ...
- 第1章 游戏之乐——快速找出故障机器
转载:编程之美_1.5_快速找出机器故障 题目:假设一个机器只存储一个标号为ID的记录,假设每份数据保存2个备份,这样就有2个机器存储了相同的数据.其中ID是小于10亿的整数 问题1.在某个时间,如果 ...
- 【编程之美】24点游戏
转自:http://blog.csdn.net/tianshuai1111/article/details/7713640 一,概述 二十四点是一种益智游戏,它能在游戏中锻炼人们的心算,它往往要求人们 ...
- 编程之美——4.11 扫雷游戏的概率
http://blog.csdn.net/fivedoumi/article/details/7705073 题目说, 一局16×16的扫雷游戏刚开始, 只翻开了两格, 分别显示数字1和2, 如下图所 ...
- 编程之美-第3章 结构之法
3.1. 字符串移位包含问题 方法1: 分别对字符串进行循环移1位,2位,3位-,来判断给定的字符串是否是其中一个字串. 复杂度是O(n^3) 方法2: 这也是一种利用空间换时间的方法. 代码如下, ...
- 2013编程之美资格赛【传话游戏】
时间限制: 1000ms 内存限制: 256MB 描述 Alice和Bob还有其他几位好朋友在一起玩传话游戏.这个游戏是这样进行的:首先,所有游戏者按顺序站成一排,Alice站第一位,Bob站最后一位 ...
- 编程之美 set 17 拈游戏分析 (2)
题目 有 N 块石头河两个玩家 A 和 B. A 先将石头分成若干堆, 然后按照 BABABA... 的顺序轮流取石块, 能将剩下的石头依次取光的玩家获胜. 每次取石头时, 每个玩家只能取一堆的 m( ...
最新文章
- [LeetCode] 102. Binary Tree Level Order Traversal_Medium tag: BFS
- 文字链接_新生命道目录及音频、文字链接(20200501更新)
- 什么?物联网方向也能发论文了?
- MSP430F5529 DriverLib 库函数学习笔记(八)模数转换模块(ADC12)
- postgresql 高可用 etcd + patroni 之六 callback bind vip
- 3.建造者模式(Builder)
- hadoop linux 集群提交任务
- 算法工程师面试备战笔记7_数据清洗与特征处理
- 计算机在线拍照解答,摄影景深在线计算器
- python+selenium常见坑
- 纬地道路纵断面设计教程_如何高效算量?市政道路从识图算量到施工工艺,一篇全搞定!...
- 自适应求积算法 MatLab版
- 如何清理卸下应用的残余文件_怎么清理手机卸载残留 需要技巧
- Python基础学习之 import 用法详解
- unity材质球发光_Unity3D - 发光材质(Emissive Materials)
- python_视频中语音识别转出文本
- GPS定位为什么需要4颗以上卫星?
- 开发类似斗鱼虎牙的运营级直播平台多少钱
- Post请求的两种编码格式:application/x-www-form-urlencoded和multipart/form-data
- 【PHP项目部署一】PHP环境配置
热门文章
- 上帝掷骰子:APP Store是赌场不是金矿
- 解决IntelliJIdea许可证过期,输入许可证认证不成功
- 技术教程 | 基于 Web 端的屏幕共享实践
- 某校2019专硕编程题-队列
- 【穿越百年咖啡厅Café logo,经典不败设计 】
- H5项目中通过iframe引入语音导览解决微信jsapi关于同一级域名二级域名跨域问题解决方案
- Hex文件和bin文件以及flash大小关系
- .msu格式文件跳过windowupdate检测直接安装方案(vs2015安装提示0x80240037安装失败,KB2999226无法安装)
- html5图片上传与预览实现
- e3服务器性能怎么样,3.5GHz发飙 至强E3-1280处理器深度评测