算法设计与分析——第三篇,倒推蛮力什么的

写在前面的话——

这次主要是就是开始讲算法了,主要的来说,主要是分治法、贪婪法还有动态规划,这些我觉得是一种处理问题的思想,还有什么蛮力法,倒推法什么的,也算是思想,但是更多的,这个也算是一种工具,会比较常见的用在之前的三种方法中,特别是倒推,其实我也觉得倒推并没有多神奇,毕竟我们做数学题的时候,就是很多时候按照正常的顺序思考不出来,反着推就能比较顺利,而且我觉得这些个算法,只能算是训练,而实际应用的时候,应该还是适可而止,不要过分强求,实际问题一般都比这些训练题复杂得多,所以我始终觉得这个只是锻炼一个思想而已!


第二篇——

我觉得下面三个题比较简单,就是第一个题有点难度,不过因为是书上例题,所以并没有花很多功夫去思考,反过来想的话,这个题也没法想的太细,因为需要很多数学思想的支撑,特别是一开始要建立一个数学模型,所以掌握方法领悟技巧就好,至于为什么要这么算,凭什么这么算就是最节约的,不讨论比较好。


1.穿越沙漠问题

一辆吉普车穿越1000千米的沙漠。吉普车的总装油量为500加仑,耗油量为1加仑/千米。由于沙漠中没有油库,必须先用这辆车在沙漠中建立临时油库。若吉普车用最少的耗油量穿越沙漠,应在那些地方建立油库,以及各处存储的油量。

 

算法设计:

这个题由于要求用最少的耗油量,也就是最后穿越沙漠之后刚好油量为0,同时,很明显,直接开是无法穿越的,在中间架设油库,只能使用这辆车,一次最所携带500加仑并且行进返回都需要耗油,如果正向分析问题会非常复杂无从下手,但是倒推会发现还是有迹可寻。

从终点倒推,刚好油量为0,则在离终点500千米的地方A建立一个油库并且存油500加仑,设A点之后的油库在B点,从B点到A点运油,满足条件的最佳方案是B点到A点共走三次,第一、二次来回耗油量为装载量的2/3,储油量为装载量的1/3,第三次单向行驶耗油量为装载量的1/3,储油量为装载量的2/3。统计一下,B点的储油量为1000加仑,此段长度为500/3千米。

同理分析B点之后的油库C点,应该需要往返5次,位置就在距离B点500/5千米的位置,存油量为1500,以此类推

 

代码如下:


#include <stdio.h>
 
int main()
{
       int dis[100],k,oil[100];
       dis[1]= 500;
       k= 1;
       oil[1]= 500;
       do {
              k++;
              dis[k]= dis[k-1] + 500/(2 * k - 1);
              oil[k]= 500 * k;
       }while(dis[k] < 1000);
 
       oil[k]= 500 * (k - 1) + (1000 - dis[k]) * (2 * k - 1);
       dis[k]= 1000;
 
       int i = 0;
       for(;i<k;i++) {
              printf("第%d个临时油库在距离起点%d的位置,储油量为%d\n",i+1,1000-dis[k-i],oil[k-i]);
       }
 
       return 0;
}



算法分析:通过倒推法可以解决一些正向推导很难处理的问题,非常有效。

 

2.54张扑克牌,两个人轮流拿牌,每个人每次最少取1张,最多取4张,谁拿最后一张谁输。编写模拟计算机先拿牌且必胜的算法。

 

算法设计:

根据题意,必须先拿且必胜,则可以理解为,在54张减去第一次拿的牌数后,每轮对方先拿,自己后拿,直到最后一轮,刚好剩下1张牌,且对方的拿牌数为一个1-4的随机数,那么此时应该考虑,对方每次的拿牌数是不能确定的,但是每轮的拿牌数是可以确定的,就是可以在对方拿完之后,自己拿一定数量的牌凑成一个可以一定达成的数字,这个数字很明显应该是5,即每轮拿5张牌,最后还剩一张,且之前减去了已经拿走的牌,则很明显,先拿走3张之后,还剩51张,每轮拿走5张,十轮之后,还剩一张,此时必定获胜。

 

代码如下:


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
       int i = 3;//计算机初始拿的牌数
       int n = 54;//当先的牌数量
       int i_times = 1;//总计拿了多少次
       int p;//另一方的拿牌数,在1-4之间随机生成一个数
       srand(time(0));
       while(n > 0)
       {
              p= rand()%4 + 1;
              printf("the%d time computer select %d paper and you %d paper\n",i_times,i,p);
              n-= i+p;
              printf("nowthe paper has left %d\n",n);
              i_times++;
              i= 5 - p;//需要在对方拿牌之后,再取的牌数和之前的牌数凑成固定的5张
              if(n - i == 1) {//如果还剩一张,此时不可以继续随机了,只能拿这张牌
                     printf("the%d time computer select %d paper and you have the last one\n",i_times,i);
                     break;
              }
       }
       return 0;
}



算法分析:这个算法的关键在于如何保证最后一定剩一张牌给另一方,而且要保证每一轮拿牌保证一样的数量才可以进行控制,但是每一轮先拿牌的话,就无法控制,只能再对方先拿牌之后才能根据他的数量再决定该拿多少张,此时根据规则,必须先拿又必须赢,此时需要考虑先拿多少张,然后开始每一轮拿牌,最后剩一张牌,顺利写出代码

 

 

3.有一堆棋子,2枚2枚的数,最后余1枚;3枚3枚的数,最后余2枚;5枚5枚的数,最后余4枚;6枚6枚的数,最后余5枚;7枚7枚的数,最后正好数完。变成求出这堆棋子最少有多少枚棋子。

 

算法设计:

从题目来看,很明显是求一个数,这个数对2求余得1,对3求余得2,对5求余得4,对6求余得5,对7整除,使用蛮力法,从最小的数7开始累加,直到满足条件结束

 

代码如下:


int main()
{
 
       int i = 7;
       while(1) {
              if(5 == i % 6 && 4 == i% 5 && 2 == i % 3 &&1 == i % 2) {
                     printf("这堆棋子的数量为%d\n",i);
                     break;
              }else
                     i+= 7;
       }
 
       return 0;
}



算法分析:以上实际采用枚举法,将每一个数都进行运算求得余数进行比对,直到符合所有条件的数出现为止,这里简单设计了下,不用每个数都判断,只需要判断7的倍数即可,可以减少循环次数,整个算法看起来比较清晰简洁。

 

 



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值