300 likes | 459 Views
搜索例题讲解(威力加强版). 陈才斌 修改自梁锋课件. 6.4 王伯买鱼. 规定 最多30种鱼 对于每种鱼,最多只能买一条 有些鱼不能一起买 每条鱼有一定价钱,王伯的资金有限 目标 买尽可能多的鱼. 12. 9. 3. 5. 7. 11. 8. 王伯买鱼. 构造图 把每种鱼看成一个顶点 顶点所代表的鱼的价格作为它的权值 两种鱼之间不能共处则在它们之间连一条边. 王伯买鱼. 用集合表示王伯买了哪些鱼 表:List = {1, 2, 4, …} 向量:Vector = {1, 1, 0, 1, …}
E N D
搜索例题讲解(威力加强版) 陈才斌修改自梁锋课件
6.4 王伯买鱼 • 规定 • 最多30种鱼 • 对于每种鱼,最多只能买一条 • 有些鱼不能一起买 • 每条鱼有一定价钱,王伯的资金有限 • 目标 • 买尽可能多的鱼
12 9 3 5 7 11 8 王伯买鱼 • 构造图 • 把每种鱼看成一个顶点 • 顶点所代表的鱼的价格作为它的权值 • 两种鱼之间不能共处则在它们之间连一条边
王伯买鱼 • 用集合表示王伯买了哪些鱼 • 表:List = {1, 2, 4, …} • 向量:Vector = {1, 1, 0, 1, …} • 计算机的数组:int got[30]; got[i] = 1 表示买了第i条鱼 got[i] = 0 否则
3 1 5 4 2 3 王伯买鱼 {0, 0, 0} - 0 {0, 0, 0} - 0 {1, 0, 0} - 3 {0, 0, 0} - 0 {0, 1, 0} - 4 {1, 0, 0} - 3 {1, 1, 0} - 7 {0, 0, 0} - 0 {0, 1, 0} - 4 {1, 0, 0} - 3 {1, 1, 0} - 7 {0, 0, 1} - 5 {0, 1, 1} - 9 {1, 0, 1} - 8 {1, 1, 1} - 12
王伯买鱼 • void buy ( i ) • if (i>n) 找到一个子集; • else • got[i] = 0; • buy( i+1 ); • if ( i能与已买的鱼共处 && 还有钱支付 ) • got[i] = 1; 并支付鱼的价钱; • buy( i+1 ); • got[i] = 0; 并取回鱼的价钱;
王伯买鱼 • 有n种鱼:2n个子集 • 230 = 1,073,741,824 • 把不必要的子集删除 • 可行性:发生冲突、不够钱 • 最优化:估算剩下的鱼,最多可以取多少
王伯买鱼 • 不考虑价格,最多能买多少的鱼? • 删除多少个顶点后剩下的图没有边 • 尽可能删度数大的顶点 • 2*边数 = Σ顶点度 • 认为删一个点就可以删与之度数相同的边数 {3, 2, 2, 2, 1, 1, 1}
6.5 数学家旅游 • 旅行线路要求 • 每次走的方向和前一次不同 • 每次走的距离比前一次长一步; • 不重复走同一个地方 • 要走回出发点 • 某些地方不能走(工地) • 最大边<=20 • 找出所有行走路线
数学家旅游 • 将城市看作一个坐标网格 • 起始位置为坐标原点 • 所走的每一段可以看作一条线段 • 不走同一个地方 任两条线段不重复 • 回到出发点 最后一点返回原点 • 不经过施工地点 点不可在线段上
数学家旅游 • 用数组表示网格 • 2 + 4 + 8 + … + 20 = 110 • int city[220][220]; • 假设O(110,110) • 行走路线 • 线段 —— 端点的坐标(x,y) —— 所有经过的格点 • 工地和经过的格点都是一样的,不可再走 • city[x][y] = 0 可以经过 • city[x][y] > 0 不可以经过
数学家旅游 • void search( now(x,y) ) • if (StepLength == n && 返回起点) • tot++; • StepLength++; • for ( 向左右两个方向 ) • if ( 可以往该方向走StepLength步 ) • 行走到dest(x’,y’)并标记走过的路线; • Search( dest(x’,y’) ); • 擦除这一步走过的线路,返回now(x,y); • StepLength--;
StepLength = m 数学家旅游 • 当第m步向上走达(x,y) • 水平方向 • m+1, m+3, … (<=n) • 垂直方向 • m+2, m+4, … (<=n) • 不考虑工地和路线重叠 • m=2: ±3±5±7 == x
数学家旅游 • 令can[m][k]=1或0表示在数轴上连续走m,m+2,m+4…, (<=n)能否到达k • can[m][k] = can[m][-k] • can[n][n] = can[n-1][n-1] = 1 • can[m][k] = can[m+2][±k±m] • 判断 • can[m+1][x]==1 && can[m+2][y]==1 • 其它方向的行走与此类似
6.9 骑士问题 • 假设 • 8*8的国际象棋棋盘 • 不能越过有障碍物的格子“b” • 马从‘n’跳到‘N’ • 要求 • 最小的步数
骑士问题 • 问题的表示 • 马的位置:struct Pos { int x,y; } • 整个棋盘:int board[x][y];它的值可以表示到达当前格子的最少步数 • 求最短路 —— 广度搜索 • 对比:6.1马的走法
骑士问题 • void WFS () • 初始化多列 • head = 0; • tail = 1; • queue[head] = 起始位置; • visited[起始位置] = 1; • step = 0; • while ( head < tail ) • step++; • for ( queue中head到tail区间的结点 ) • 扩展节点 —— 向八个方向跳一步 • 如果马没有跳出棋盘,并且到达一个新的位置,则入队列 • 如果到达目标则停止 • 返回路径不存在
6.11 过河 • 河中有n支木桩 • 在0时刻都是降的状态 • 升起A分钟,降下B分钟,升起A分钟…… • 农夫开始在左岸(0时刻) • 每一时刻可以到左右跳最多5个桩的距离 • 要求:用最少时间到达右岸 左岸 右岸
过河 t=0 L 1 2 3 4 5 6 7 8 10 R …… ? t=1 L 1 2 3 4 5 6 7 8 10 R …… ? t=2 L 1 2 3 4 5 6 7 8 10 R …… ? t=2 L 1 2 3 4 5 6 7 8 10 R …… ? t=4 L 1 2 3 4 5 6 7 8 10 R …… …… …… ……
过河 • 问题:时间无限导致图是无限的 • 无限图搜索 • 空间无法承受 • 导致程序无法结束
过河 • 设第i支桩有A[i]时间升,B[i]时间降 • 周期:T[i] = A[i]+B[i] • 在时刻t:w = t % T[i] • 如果1<=w<=A[i],桩是升起的否则桩降下去,特别的当w=0时 • 令tf = LCM( T[1], …, T[n] ) • tf % T[i] = 0,即所有桩都降下 • T[i]<=10,所以 tf <= LCM(1, …, 10) = 2520
过河 • time = 0; • do • time++; • if (到达右岸) 成功并退出搜索; • for (0<=i<=n+1) • for (i-5<=j<=i+5) • if (在当前时间桩i是可站立的 && 上一时刻能够到达桩j) • 当前时刻可到达桩i; • while ( time <= 2520 ); • 返回:无法到达右岸
6.8 分球 • 任意两个相邻的非空的盒子里的球可以移动到两个相邻的空盒中 • 移动不能改变这两个球的排列顺序 • 目标: • 用最少的次数把所有的a都移到b的左边
分球 • 状态的总数 • 先不考虑空盒,则剩下2N-2个球排成一排 • 再放入空盒,共有2N-1个位置可以放 • N=7时,总共有12012种状态
分球 • 状态的保存 • 空盒的位置 + a球的位置 + b球的位置 • 数据结构 • struct State { int blank; //空盒位置 int a[6]; //拿走空盒后,所有a球的位置 int father; //指向父结点的位置}
分球 • 重复结点的判断 • 映射 F : State Int (Int是非负整数集) • T : State.a Int • F ’ : State (blank, Int) • State F F ’ • OOab 0 (0, 0) • OOba 1 (0, 1) • aOOb 2 (1, 0) • bOOa 3 (1, 1) • abOO 4 (2, 0) • baOO 5 (2, 1)
分球 • 计算: T : State.a Int
分球 • a[i]表示除去空盒后第i个a球所在的位置 • baabOOaabb • a[1..4] = {2, 3, 5, 6} 2N-2-k个球,其中N-1-i个a k
总结 • 明确状态表示 • 决定搜索方式 • 编写程序 • 性能的优化
吃饭时间 求BG