根据客观事物的特性,分析其内部的机理,弄清关系,在适当抽象的条件下,得到可以描述事物属性的数学工具。
经过数学分析,如果能够抽象出Ad Hoc类问题的内在规律,则可以采用机理分析法建立数学模型,然后根据模型的原理对应到算法,编程实现,通过执行算法得到问题解,如图1.1-1所示。 机理分析法的核心是数学建模,即使用适当的数学思想建立起模型;或者提取问题中的有效信息,用简明的方式表达其规律。需要注意的是: 1)选择的模型必须尽量多地体现问题的本质特征。但这并不意味着模型越复杂越好,累赘的信息会影响算法效率。 2)模型的建立不是一个一蹴而就的过程,而是要经过反复地检验和修改,在实践中不断完善。 3)数学模型通常有严格的格式,但程序编写形式可不拘一格。 机理分析法是一个复杂的数据抽象过程。我们要善于透视问题的本质,寻找突破口,进而选择适当的模型。模型的构造过程可以帮助我们认识问题,不同的模型从不同的角度反映问题,可以引发不同的思路,起到引导发散思维的作用。但认识问题的最终目的是解决问题,模型的固有性质虽可帮我们建立算法,其优劣也可通过时空复杂度等指标来分析和衡量,但最终还是以程序的运行结果为标准。所以模型不是一成不变的,同样要通过各种技术不断优化。模型的产生虽然是人脑思维的产物,但它仍然是客观事物在人脑中的反映。所以要培养良好的建模能力,还必须依靠在平时的学习中积累丰富的知识和经验。 下面举两个实验范例。 【1.1.1 Factstone Benchmark】 【问题描述】 Amtel已经宣布,到2010年,它将发行128位计算机芯片;到2020年,它将发行256位计算机;等等,Amtel坚持每持续十年将其字大小翻一番的战略。(Amtel于2000年发行了64位计算机,在1990年发行了32位计算机,在1980年发行了16位计算机,在1970年发行了8位计算机,并首先在1960年发行了4位计算机。) Amtel将使用新的标准检查等级(Factstone)来宣传其新芯片大大提高的能力。Factstone等级被定义为这样的最大整数n,使得n!可以表示为一个计算机字中的无符号整数(比如1960年的4位计算机可表示3!=6,而不能表示4!=24)。 给出一个年份1960≤y≤2160,Amtel最近发布的芯片的Factstone等级是什么? 输入: 给出若干测试用例。每个测试用例一行,给出年份y。在最后一个测试用例后,给出包含0的一行。 输出: 对于每个测试用例,输出一行,给出Factstone等级。试题来源:Waterloo local 2005.09.24在线测试地址:POJ 2661,UVA 10916试题解析对于给定的年份,先求出当年Amtel处理器的字大小,然后计算出最大的n的值,使得n!成为一个符合字的大小的无符号整数。在1960年,字的大小是4位,以后每十年字的大小翻一番,这就意味着,y年字的位数为k=22+y-196010。能放在k位中最大的无符号整数是2k-1。如果n!为不大于2k-1的最大正整数,则n为y年芯片的Factstone等级。计算方法有两种:方法1:直接求不大于2k-1的最大正整数n!,这种方法极容易溢出且速度慢。方法2:采用对数计算,即根据log2n!=log2n+log2(n-1)+…+log21≤log2(2k-1)计算y年字的位数k,累加log2i(i从1出发,每次加1),直到数字超过k为止。此时,i-1即为Factstone等级。程序清单
#include <stdio.h> #include <math.h> int y,Y,i,j,m; // 年份为y double f,w;// y年字的位数为w,log2i的累加值为f main(){ while (1 == scanf("%d",&y) && y){// 输入年份y w = log(4);// 按照每十年字的大小翻一番的规律,计算y年字的位数w for (Y=1960; Y<=y; Y+=10){ w *= 2; } i = 1;// 累加log2i(每次i加1),直到数字超过w f = 0; while (f < w) { f += log((double)++i); } printf("%d\n",i-1) ;// 输出Factstone等级 } if (y) printf("fishy ending %d\n",y); }【1.1.2 Bridge】【问题描述】n个人要在晚上过桥,在任何时候最多两人一组过桥,每组要有一只手电筒。在这n个人中只有一只手电筒可以用,因此要安排以某种往返的方式来返还手电筒,使得更多的人可以过桥。每个人的过桥速度不同,每组的速度由速度较慢的成员所决定。请确定一个策略,让n个人用最少的时间过桥。输入:输入的第一行给出n,接下来的n行给出每个人的过桥时间,不会超过1000人,且没有人的过桥时间超过100秒。输出:输出的第一行给出所有n个人过桥的总的秒数,接下来的若干行给出实现策略。每行包含一个或两个整数,表示组成一组过桥的一个人或两个人(每个人用其在输入中给出的过桥所用的时间来标识。虽然许多人的过桥时间相同,但即使有混淆,对结果也没有影响)。这里要注意的是过桥也有方向性,因为要返还手电筒让更多的人通过。如果用时最少的策略有多个,则任意一个都可以。
试题来源:Waterloo local 2000.09.30在线测试地址:POJ 2573,ZOJ 1877,UVA 10037试题解析稍动脑筋,便可以得出一个简单的逻辑:要使得n个人用最少的时间过桥,慢的成员必须借助快的成员传递手电筒。由于一次过桥最多两人且手电筒需要往返传递,因此以两个成员过桥为一个分析单位,计算过桥时间。我们按过桥时间递增的顺序将n个成员排序。设当前序列中,A是最快的人,B是次快的人,A和B是序列首部的两个元素。a是最慢的人,b是次慢的人,a和b是序列尾部的两个元素。有两种过桥方案:方案1:用最快的成员A传递手电筒帮助最慢的a和b过桥。如果带一个最慢的成员,则所用的时间是a+A(a表示最快和最慢的两个成员到对岸所需的时间,而A是最快的成员返回所需的时间)。显然带两个最慢的成员所用的时间是2*A+a+b。方案2:用最快的成员A和次快的成员B传递手电筒帮助最慢的a和b过桥。步骤1:A和B到对岸,所用时间为B。步骤2:A返回,将手电筒给最慢的a和b,所用时间为A。步骤3:a和b到对岸,所用时间为a;到对岸后,他们将手电筒交给B。步骤4:B需要返回原来的岸边,因为要交还手电筒,所需时间为B。所以,需要的总时间为2*B+A+a。显然,最慢的a和b要用最少的时间过桥,只能借助A或者A和B传递手电筒过桥,其他方法都会增加过桥时间。哪一种过桥方案更有效?比较一下就行了:如果(2A+a+b<2B+A+a),则采用第1种方案,即用最快的成员A传递手电筒;否则采用第2种方案,即用最快的成员A和次快的成员B传递手电筒(2A+a+b<2B+A+a等价于b+A<2*B)。我们每次帮助最慢的两个成员过桥(n-=2),累计每个最佳过桥方案的时间。最后产生两种可能情况:1)对岸剩下2个队员(n==2),全部过桥,即累计时间为B。2)对岸剩下3个队员(n==3),用最快的成员传递手电筒,帮助最慢的成员过桥,然后与次慢的成员一起过桥,即累计时间为a+A+b。程序清单
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> using namespace std; int n,i,j,k,a[111111]; // 人数为n,每个人的速度存储于序列a[] int ans=0;// n个人过桥的总时间初始化 int main () { scanf("%d",&n);// 输入每个人的速度 for(i=1;i<=n;i++)scanf("%d",a+i); if(n==1){// 输出1个人的过桥方案 printf("%d\n%d\n",a[1],a[1]);return 0; } int nn=n; sort(a+1,a+n+1);// 按照速度递增顺序排序 while(n>3){// 统计n个人过桥的总时间 if(a[1]+a[n-1]<2*a[2]){// 累计用a[1]传递手电筒帮助最慢的2个成员过桥 // 所需的时间 ans+=a[n]+a[1]*2+a[n-1]; }else{// 累计用a[1]a[2]传递手电筒帮助最慢的2个成员 // 过桥所需的时间 ans+=a[2]+a[1]+a[2]+a[n]; } n-=2;// 两个最慢的成员过桥 } if(n==2)ans+=a[2];// 对岸剩下2个成员,累计其过桥的时间 else ans+=a[1]+a[2]+a[3];// 对岸剩下3个成员,累计其过桥的时间 printf("%d\n",ans);// 输出n个人过桥的总时间 n=nn; while(n>3){// 输出每组人过桥所用的时间 if(a[1]+a[n-1]<2*a[2])// 输出用a[1]传递手电筒的过桥方案 printf("%d %d\n%d\n%d %d\n%d\n",a[1],a[n],a[1],a[1],a[n-1],a[1]); else// 输出用a[1]和a[2]传递手电筒的过桥方案 printf("%d %d\n%d\n%d %d\n%d\n",a[1],a[2],a[1],a[n-1],a[n],a[2]); n-=2;// 两个最慢的成员过桥 } if(n==2)printf("%d %d\n",a[1],a[2]);// 剩下2个队员过桥,输出过桥方案 else// 剩下3个队员过桥,输出过桥方案 printf("%d %d\n%d\n%d %d\n",a[1],a[3],a[1],a[1],a[2]); return 0; } 相关资源:七夕情人节表白HTML源码(两款)