在一些游戏中,通过递归或者回溯的方式,可以实现一些AI,比如我曾经玩过的企鹅推箱子,游戏中的AI可以帮你自动搜索出一条最适合你的路径。这里,是 男人就下一百层的AI,输入和输出我就不给出了,主要从算法的思想层面来分析这个游戏(该AI可以计算出该小人从跳落至到达地面的最短时间,这里乃有,如 果一次跳得很高,可能会被摔死)。
在实际的游戏中,还有类似于滑轮的砖块以及带上锯齿的砖块,分析起来会有一些复杂,而且,如果涉及到的话,本文的思想可能无法体现到那种游戏中,所以,复杂的情况暂时舍弃。
游戏来自POJ,源代码有若干亮点:
(1) 小人最开始的时候有可能在第一块板子上,也有可能在空中,分类的话有些麻烦。这里,不如假设此人的脚下有一个虚拟的板子,这块板子记为(第0块),有点像是哨兵位。那么,小人落到第一块板子的时候,如果直接就在板子上,可以记为从第0 块板子经过0秒落到第一块板子上。
(2) 由于从第一块板子跳落到左边板子和右边板子的这两个问题几乎相同,而这两个子问题的任意一个发展到后面又会和之前的完全一样,所以,考虑到子问题的相似 性,这里可以想到利用牺牲空间来换时间的方法来解决问题,毕竟,如果所有情况都进行尝试的话,结果将会是O(2^n)(n为板子的数目)
(3) 每一个子问题的具体解决方法(算最短时间,左边和右边的应该一样):
好了,附上源代码吧,原文的代码上木有解析,我这里补上:
2 #include<memory.h>
3 #include<stdlib.h> // 为了开启 Mycompare和memset
4
5 #define MAX_N 1000
6 #define INFINITE 1000000 // 很大的数,展示不可能性
7
8 int t,n,x,y,max; // t为时间,n为板子的数目,max为每次可以跳的最大高度
9
10 struct Platform
11 {
12 int lx,rx,h;
13 }; // 关于平板的结构体
14
15 Platform aPlatform[MAX_N+ 10];
16 int aLeftMinTime[MAX_N+ 10];
17 int aRightMinTime[MAX_N+ 10];
18
19 int MyCompare( const void *e1, const void *e2)
20 {
21 Platform *p1,*p2;
22 p1=(Platform *)e1;
23 p2=(Platform *)e2;
24 return p2->h-p1->h; // 作为后面用memset的铺垫,从大到小排序
25 }
26
27 int MinTime( int L, bool bLeft) // 递归函数
28 {
29 int y=aPlatform[L].h;
30 int x,i;
31 if(bLeft) // 判断上一次掉落是左边落下的还是右边落下的
32 x=aPlatform[L].lx;
33 else
34 x=aPlatform[L].rx;
35 for(i=L+ 1;i<=n;i++)
36 {
37 if(aPlatform[i].lx<=x&&aPlatform[i].rx>=x)
38 break;
39 } // 寻找一个适合降落的点,至上而下寻找
40 if(i<=n)
41 {
42 if(y-aPlatform[i].h>max)
43 return INFINITE;
44 }
45 else
46 {
47 if(y>max)
48 return INFINITE;
49 else
50 return y; // 这里处理最后一次跳落
51 }
52 // 每次过程都有一个竖过程和一个横过程
53 int nLeftTime=y-aPlatform[i].h+x-aPlatform[i].lx;
54 int nRightTime=y-aPlatform[i].h+aPlatform[i].rx-x;
55 // 利用这两个数组记录子结果,节约了不少时间
56 if(aLeftMinTime[i]==- 1)
57 aLeftMinTime[i]=MinTime(i, true);
58 if(aRightMinTime[i]==- 1)
59 aRightMinTime[i]=MinTime(i, false);
60 // 这里的左时间和右时间应该是基于第一次的选择
61 nLeftTime+=aLeftMinTime[i];
62 nRightTime+=aRightMinTime[i];
63 if(nLeftTime<nRightTime)
64 return nLeftTime;
65 return nRightTime;
66 }
67
68 int main()
69 {
70 scanf( " %d ",&t);
71 for( int i= 0;i<n;i++)
72 {
73 memset(aLeftMinTime,- 1, sizeof(aLeftMinTime));
74 memset(aRightMinTime,- 1, sizeof(aRightMinTime));
75 scanf( " %d%d%d%d ",&n,&x,&y,&max);
76 // 比较有创意的第0个板子
77 aPlatform[ 0].lx=x;
78 aPlatform[ 0].rx=x;
79 aPlatform[ 0].h=y;
80 for( int j= 1;j<=n;j++)
81 scanf( " %d%d%d ",&aPlatform[j].lx,&aPlatform[j].rx,&aPlatform[j].h);
82 // 用自定义的排序方式进行快排
83 qsort(aPlatform,n+ 1, sizeof(Platform),MyCompare);
84 // 输出,最初从左边跳和右边跳是一回事
85 printf( " %d\n ",MinTime( 0, true));
86 }
87 return 0;
88 }