unity3D教程 : 游戏算法开发(三)

六、贪婪法

    贪婪法是一种不追求最优解,只希望得到较为满意解的方法。贪婪法一般可以快速得到满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪婪法常以当前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪婪法不要回溯。

    例如平时购物找钱时,为使找回的零钱的硬币数最少,不考虑找零钱的所有各种发表方案,而是从最大面值的币种开始,按递减的顺序考虑各币种,先尽量用大面值 的币种,当不足大面值币种的金额时才去考虑下一种较小面值的币种。这就是在使用贪婪法。这种方法在这里总是最优,是因为银行对其发行的硬币种类和硬币面值 的巧妙安排。如只有面值分别为1、5和11单位的硬币,而希望找回总额为15单位的硬币。按贪婪算法,应找1个11单位面值的硬币和4个1单位面值的硬 币,共找回5个硬币。但最优的解应是3个5单位面值的硬币。

    【问题】 装箱问题

    问题描述:装箱问题可简述如下:设有编号为0、1、…、n-1的n种物品,体积分别为v0、v1、…、vn-1。将这n种物品装到容量都为V的若干箱 子里。约定这n种物品的体积均不超过V,即对于0≤i<n,有0<vi≤V。不同的装箱方案所需要的箱子数目可能不同。装箱问题要求使装尽这n种物品的箱 子数要少。

    若考察将n种物品的集合分划成n个或小于n个物品的所有子集,最优解就可以找到。但所有可能划分的总数太大。对适当大的n,找出所有可能的划分要花费的时 间是无法承受的。为此,对装箱问题采用非常简单的近似算法,即贪婪法。该算法依次将物品放到它第一个能放进去的箱子中,该算法虽不能保证找到最优解,但还 是能找到非常好的解。不失一般性,设n件物品的体积是按从大到小排好序的,即有v0≥v1≥…≥vn-1。如不满足上述要求,只要先对这n件物品按它们的 体积从大到小排序,然后按排序结果对物品重新编号即可。装箱算法简单描述如下:

[AppleScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{ 输入箱子的容积;
 
 
输入物品种数n;
 
 
按体积从大到小顺序,输入各物品的体积;
 
 
预置已用箱子链为空;
 
 
预置已用箱子计数器box_count为 0
 
 
for ( i = 0 ;i   { 从已用的第一只箱子开始顺序寻找能放入物品i 的箱子j;
 
 
if (已用箱子都不能再放物品i)
 
 
{ 另用一个箱子,并将物品i放入该箱子;
 
 
box_count + +
 
 
}
 
 
else
 
 
将物品i放入箱子j;
 
 
}
 
 
}


    上述算法能求出需要的箱子数box_count,并能求出各箱子所装物品。下面的例子说明该算法不一定能找到最优解,设有6种物品,它们的体积分别为: 60、45、35、20、20和20单位体积,箱子的容积为100个单位体积。按上述算法计算,需三只箱子,各箱子所装物品分别为:第一只箱子装物品1、 3;第二只箱子装物品2、4、5;第三只箱子装物品6。而最优解为两只箱子,分别装物品1、4、5和2、3、6。

    若每只箱子所装物品用链表来表示,链表首结点指针存于一个结构中,结构记录尚剩余的空间量和该箱子所装物品链表的首指针。另将全部箱子的信息也构成链表。以下是按以上算法编写的程序。

    【程序】
[AppleScript]  纯文本查看 复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# include
 
  
   # include
 
  
  typedef struct ele
 
  
   { int vno;
 
  
  struct ele * link;
 
  
   } ELE;
 
  
  typedef struct hnode
 
  
   { int remainder;
 
  
  ELE * head;
 
  
  Struct hnode * next;
 
  
   } HNODE;
 
  
  void main ( )
 
  
   { int n , i , box_count , box_volume , * a;
 
  
  HNODE * box_h , * box_t , * j;
 
  
  ELE * p , * q;
 
  
  Printf ( “输入箱子容积\n” ) ;
 
  
  Scanf ( “%d” , & box_volume ) ;
 
  
  Printf ( “输入物品种数\n” ) ;
 
  
  Scanf ( “%d” , & n ) ;
 
  
  A = ( int * ) malloc ( sizeof ( int ) * n ) ;
 
  
  Printf ( “请按体积从大到小顺序输入各物品的体积:” ) ;
 
  
  For ( i = 0 ;i   Box_h = box_t = NULL;
 
  
  Box_count = 0 ;
 
  
  For ( i = 0 ;i   { p = ( ELE * ) malloc ( sizeof ( ELE ) ) ;
 
  
  p - > vno = i;
 
  
   for ( j = box_h;j! = NULL;j = j - > next )
 
  
   if ( j - > remainder > = a[i] ) break;
 
  
   if ( j = = NULL )
 
  
   { j = ( HNODE * ) malloc ( sizeof ( HNODE ) ) ;
 
  
  j - > remainder = box_volume - a[i];
 
  
  j - > head = NULL;
 
  
   if ( box_h = = NULL ) box_h = box_t = j;
 
  
   else box_t = boix_t - > next = j;
 
  
  j - > next = NULL;
 
  
  box_count + + ;
 
  
   }
 
  
   else j - > remainder - = a[i];
 
  
   for ( q = j - > next;q! = NULL & & q - > link! = NULL;q = q - > link ) ;
 
  
   if ( q = = NULL )
 
  
   { p - > link = j - > head;
 
  
  j - > head = p;
 
  
   }
 
  
   else
 
  
   { p - > link = NULL;
 
  
  q - > link = p;
 
  
   }
 
  
   }
 
  
  printf ( “共使用了%d只箱子”,box_count ) ;
 
  
  printf ( “各箱子装物品情况如下:” ) ;
 
  
   for ( j = box_h , i = 1 ;j! = NULL;j = j - > next , i + + )
 
  
   { printf ( “第% 2 d只箱子,还剩余容积% 4 d,所装物品有;\n” , I , j - > remainder ) ;
 
  
   for ( p = j - > head;p! = NULL;p = p - > link )
 
  
  printf ( “% 4 d” , p - > vno + 1 ) ;
 
  
  printf ( “\n” ) ;
 
  
   }
 
  
   }


    【问题】 马的遍历

    问题描述:在8×8方格的棋盘上,从任意指定的方格出发,为马寻找一条走遍棋盘每一格并且只经过一次的一条路径。

    马在某个方格,可以在一步内到达的不同位置最多有8个,如图所示。如用二维数组board[ ][ ]表示棋盘,其元素记录马经过该位置时的步骤号。另对马的8种可能走法(称为着法)设定一个顺序,如当前位置在棋盘的(i,j)方格,下一个可能的位置依 次为(i+2,j+1)、(i+1,j+2)、(i-1,j+2)、(i-2,j+1)、(i-2,j-1)、(i-1,j-2)、(i+1,j-2)、 (i+2,j-1),实际可以走的位置尽限于还未走过的和不越出边界的那些位置。为便于程序的同意处理,可以引入两个数组,分别存储各种可能走法对当前位 置的纵横增量。

    4 3

    5 2

    马

    6 1

    7 0

    对于本题,一般可以采用回溯法,这里采用Warnsdoff策略求解,这也是一种贪婪法,其选择下一出口的贪婪标准是在那些允许走的位置中,选择出口最少 的那个位置。如马的当前位置(i,j)只有三个出口,他们是位置(i+2,j+1)、(i-2,j+1)和(i-1,j-2),如分别走到这些位置,这三 个位置又分别会有不同的出口,假定这三个位置的出口个数分别为4、2、3,则程序就选择让马走向(i-2,j+1)位置。

    由于程序采用的是一种贪婪法,整个找解过程是一直向前,没有回溯,所以能非常快地找到解。但是,对于某些开始位置,实际上有解,而该算法不能找到解。对于 找不到解的情况,程序只要改变8种可能出口的选择顺序,就能找到解。改变出口选择顺序,就是改变有相同出口时的选择标准。以下程序考虑到这种情况,引入变 量start,用于控制8种可能着法的选择顺序。开始时为0,当不能找到解时,就让start增1,重新找解。细节以下程序。

    【程序】

[AppleScript]  纯文本查看 复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# include
 
  
  int delta_i[ ] = { 2 , 1 , -1 , -2 , -2 , -1 , 1 , 2 } ;
 
  
  int delta_j[ ] = { 1 , 2 , 2 , 1 , -1 , -2 , -2 , -1 } ;
 
  
  int board[ 8 ][ 8 ];
 
  
  int exitn ( int i , int j , int s , int a[ ] )
 
  
   { int i 1 , j 1 , k , count;
 
  
   for ( count = k = 0 ;k < 8 ;k + + )
 
  
   { i 1 = i + delta_i[ ( s + k ) % 8 ];
 
  
  j 1 = i + delta_j[ ( s + k ) % 8 ];
 
  
   if ( i 1 > = 0 & & i 1 < 8 & & j 1 > = 0 & & j 1 < 8 & & board[I 1 ][j 1 ] = = 0 )
 
  
  a[count + + ] = ( s + k ) % 8 ;
 
  
   }
 
  
   return count;
 
  
   }
 
  
  int next ( int i , int j , int s )
 
  
   { int m , k , mm , min , a[ 8 ] , b[ 8 ] , temp;
 
  
  m = exitn ( i , j , s , a ) ;
 
  
   if ( m = = 0 ) return 1 ;
 
  
   for ( min = 9 , k = 0 ;k   { temp = exitn ( I + delta_i[a[k]] , j + delta_j[a[k]] , s , b ) ;
 
  
   if ( temp   { min = temp;
 
  
  kk = a[k];
 
  
   }
 
  
   }
 
  
   return kk;
 
  
   }
 
  
  void main ( )
 
  
   { int sx , sy , i , j , step , no , start;
 
  
   for ( sx = 0 ;sx < 8 ;sx + + )
 
  
   for ( sy = 0 ;sy < 8 ;sy + + )
 
  
   { start = 0 ;
 
  
  do {
 
  
   for ( i = 0 ;i < 8 ;i + + )
 
  
   for ( j = 0 ;j < 8 ;j + + )
 
  
  board[i][j] = 0 ;
 
  
  board[sx][sy] = 1 ;
 
  
  I = sx; j = sy;
 
  

你可能感兴趣的:(unity开发,unity)