吴昊品游戏核心算法 Round 18 —— 吴昊教你玩Glow Puzzle(后篇)

  

   哎呀,糟糕了呀!我忘记说了,其实最高级别不是AWESOME,而是EXCELLENT,这应该是根据时间的快慢以及是否起始点和终止点为同一个点来共同判断的吧!

  重提七座桥

 

 

  

  柯尼斯堡七桥问题图论中的著名问题。这个问题是基于一个现实生活中的事例:当时东普鲁士柯尼斯堡(今日俄罗斯加里宁格勒)市区跨普列戈利亚河两岸,河中心有两个小岛。小岛与河的两岸有七条桥连接。在所有桥都只能走一遍的前提下,如何才能把这个地方所有的桥都走遍?
  

 

  

  莱昂哈德·欧拉1735年圆满地解决了这一问题,并在第二年发表在论文《柯尼斯堡的七桥》中,证明符合条件的走法并不存在,也顺带提出和解决了一笔画问题[1]。这篇论文在圣彼得堡科 学院发表,成为图论史上第一篇重要文献。欧拉把实际的抽象问题简化为平面上的点与线组合,每一座桥视为一条线,桥所连接的地区视为点。这样若从某点出发后 最后再回到这点,则这一点的线数必须是偶数,这样的点称为偶顶点。相对的,连有奇数条线的点称为奇顶点。欧拉论述了,由于柯尼斯堡七桥问题中存在4个奇顶 点,它无法实现符合题意的遍历。
 

 

  

欧拉把问题的实质归于一笔画问题,即判断一个图是否能够遍历完 所有的边而没有重复,而柯尼斯堡七桥问题则是一笔画问题的一个具体情境。欧拉最后给出任意一种河──桥图能否全部走一次的判定法则,从而解决了“一笔画问 题”。对于一个给定的连通图,如果存在两个以上(不包括两个)奇顶点,那么满足要求的路线便不存在了,且有n个奇顶点的图至少需要n/2笔画出。如果只有 两个奇顶点,则可从其中任何一地出发完成一笔画。若所有点均为偶顶点,则从任何一点出发,所求的路线都能实现,他还说明了怎样快速找到所要求的路线。[1]

  一笔画定理的严格证明

  先定义能一笔画出并回到起点的图为欧拉图,连通就是说任意两个节点之间可以找到一条连接它们的线。这个要求看来很重要,直观方法中与这一点对应的是说原图本身不能是分成多个的。

  证明如下:

  设 G为一欧拉图,那么G显然是连通的。另一方面,由于G本身为一闭路径,它每经过一个顶点一次,便给这一顶点增加度数2,因而各顶点的度均为该路径经历此顶 点的次数的两倍,从而均为偶数。反之,设G连通,且每个顶点的度均为偶数,欲证G为一欧拉图。为此,对G的边数归纳。当m = 1时,G必定为单结点的环,显然这时G为欧拉图。设边数少于m的连通图,在顶点度均为偶数时必为欧拉图,现考虑有m条边的图G。设想从G的任一点出发,沿 着边构画,使笔不离开图且不在构画过的边上重新构画。由于每个顶点都是偶数度,笔在进入一个结点后总能离开那个结点,除非笔回到了起点。在笔回到起点时, 它构画出一条闭路径,记为H。从图G中删去H的所有边,所得图记为G’,G’未必连通,但其各顶点的度数仍均为偶数.考虑G的各连通分支,由于它们都连 通,顶点度数均为偶数,而边数均小于m,因此据归纳假设,它们都是欧拉图。此外,由于G连通,它们都与H共有一个或若干个公共顶点,因此,它们与H一起构 成一个闭路径。这就是说,G是一个欧拉图。

 

  

   针对游戏玩家的AI

  在 后篇中,我们来设计一个针对玩家的AI,对于玩家来说,如何区分关卡的难度并不是首要的,首要的问题是需要找到一个合理的解,哪怕不是最优的解,也至少是 一个合理的解,这样,用户就不用次次查攻略了,而是他掌握了一个近乎于无敌的攻略——Glow Puzzle的用户版AI。此AI和关卡设计的AI的最大区别在于,该AI假设关卡是合理的,它会返回一条路径,让玩家知道如何快速地通过此关卡。

  如图所示,此为该AI的数学模型:

 

  

  对如上的图,我们将其每个顶点进行标注,然后,读入一个游戏界面的地图(用一个二维整型矩阵来标注),我们的输出可以得到一个可行的解,以便满足玩家的要求。

  关于欧拉回路和欧拉路,在Round 17中谈《吴昊教你玩单词接龙游戏》的时候已经备述了,所以,在Round 18中不再赘述,主要说说如何从某个起始点遍历一个一笔画问题,并最终输出一个完整的解(这个解是历经整个顶点的)

  这里给出C语言的代码,源码为PASCAL的(这里就是一个翻译的过程,我是人工翻译的,目前还是有PASCAL转C语言的翻译器,只是,貌似还是不太好用,很多地方,包括编码风格啊,缩进啊都不太统一):

  两个嵌套的do--while循环略显犀利啊!

  Input:

 

 

  

  Output:
 

 

 

 1  #include<stdio.h>
 2  
 3   // 定义游戏界面的规模
 4    const  int N= 6;
 5   // 定义一张选定好的地图
 6    int array[N][N]={{ 0, 1, 0, 0, 1, 1},
 7                   { 1, 0, 1, 1, 0, 1},
 8                   { 0, 1, 0, 1, 0, 0},
 9                   { 0, 1, 1, 0, 1, 1},
10                   { 1, 0, 0, 1, 0, 1},
11                   { 1, 1, 0, 1, 1, 0}};
12  
13   int degree[N];
14   int i,j,r,sum,odt,start,now;
15  
16   int main()
17  {
18     // 总度数
19      int sum= 0;
20     // 奇点个数
21      int odt= 0;
22     // 默认以第一个结点作为起点
23      int start= 1;    
24     for(i= 1;i<=N;i++)
25    {
26      degree[i]= 0;
27       for(j= 1;j<=N;j++)
28      {
29         // 分别相加,统计每个点的度
30         degree[i]=degree[i]+array[i][j];                 
31      }                 
32      sum+=degree[i];
33       // 如果该点的度数为奇点的话
34        if(degree[i]% 2== 1)
35      {
36        odt+= 1;
37         // 以奇点作为起始点
38         start=i;                  
39      }
40    }
41     // 如果奇点大于2的话,就无解了
42      if(odt> 2) printf( " No solution! ");
43     else
44    {
45       // 将当前结点标识为now
46       now=start;    
47      printf( " %d ",start);
48       do
49      {
50         int r= 0;
51         /* 找到满足条件的下一个结点
52           下一个结点满足的条件应该是这样的:
53           (1)必须是前一个结点的邻接结点
54           (2)那一点的度数要么大于1,要么等于1就是最简单的总度数为2的情形
55          */
56         do
57        {
58          r=r+ 1;    
59        } while(array[now][r]> 0&&((degree[r]> 1)||((degree[r]== 1)&&(sum== 2))));
60         // 将这个邻接矩阵对应的两个元素清0
61         array[now][r]= 0;
62        array[r][now]= 0;
63         // 标记了那两个点之后,将总度数减2
64         sum=sum- 2;
65         // 各点度数减1
66         degree[now]--;
67        degree[r]--;
68         // 定义下一个起点
69         now=r;
70        printf( " -->%d ",r);
71      } while(sum== 0);
72    }
73     return  0;
74  }

 

 


 


 

转载于:https://www.cnblogs.com/tuanzang/archive/2013/04/24/3039746.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 吴昊 20200722041 题量: 27 满分:100.0 截止日期:2023-05-23 12:00 吴昊 39' 57'' 数据采集与网络爬虫第一次阶段测试 返回 26 交卷 已知hello.html文件的内容如下。 <html> <body> <div> <ul> <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-1"><a href="link2.html">second item </a></li> <li class="item-inactive"><a href="link3.html">third item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> </ul></div></body></html> 请使用lxml库分别按照如下要求查找hello.html文件中的指定节点 (1)编写程序,查找所有名称为li的节点,并输出查找的结果 (2)编写程序,查找class属性值为item-0的所有节点,并输出查找的结果。 (3)编写程序,查找<li>下href属性值为link1.html的名为a的子节点,并输出查找的结果
05-24
以下是三个问题的答案: (1)查找所有名称为li的节点,并输出查找的结果: ```python from lxml import etree html = ''' <html> <body> <div> <ul> <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-1"><a href="link2.html">second item </a></li> <li class="item-inactive"><a href="link3.html">third item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> </ul> </div> </body> </html> ''' # 使用lxml解析html字符串 html_tree = etree.HTML(html) # 查找所有名称为li的节点 li_nodes = html_tree.xpath('//li') # 输出查找结果 for li_node in li_nodes: print(etree.tostring(li_node, encoding='utf-8').decode('utf-8')) ``` 输出结果如下: ``` <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-1"><a href="link2.html">second item </a></li> <li class="item-inactive"><a href="link3.html">third item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> ``` (2)查找class属性值为item-0的所有节点,并输出查找的结果: ```python from lxml import etree html = ''' <html> <body> <div> <ul> <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-1"><a href="link2.html">second item </a></li> <li class="item-inactive"><a href="link3.html">third item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> </ul> </div> </body> </html> ''' # 使用lxml解析html字符串 html_tree = etree.HTML(html) # 查找class属性值为item-0的所有节点 item_0_nodes = html_tree.xpath('//li[@class="item-0"]') # 输出查找结果 for item_0_node in item_0_nodes: print(etree.tostring(item_0_node, encoding='utf-8').decode('utf-8')) ``` 输出结果如下: ``` <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> ``` (3)查找<li>下href属性值为link1.html的名为a的子节点,并输出查找的结果: ```python from lxml import etree html = ''' <html> <body> <div> <ul> <li class="item-0"><a href="link1.html">first item </a></li> <li class="item-1"><a href="link2.html">second item </a></li> <li class="item-inactive"><a href="link3.html">third item </a></li> <li class="item-0"><a href="link4.html">fourth item </a></li> <li class="item-0"><a href="link5.html">fifth item </a></li> </ul> </div> </body> </html> ''' # 使用lxml解析html字符串 html_tree = etree.HTML(html) # 查找<li>下href属性值为link1.html的名为a的子节点 a_node = html_tree.xpath('//li/a[@href="link1.html"]') # 输出查找结果 print(etree.tostring(a_node[0], encoding='utf-8').decode('utf-8')) ``` 输出结果如下: ``` <a href="link1.html">first item </a> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值