codewars 7×7 Skyscrapers 问题解决

codewars 7×7 Skyscrapers 问题解决一、背景说明二、题目分析功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导...
摘要由CSDN通过智能技术生成

1、背景说明

最近工作上的任务完成得比较超前,闲暇之余就会在刷题网站上刷题玩。前两天偶然刷到一道1kyu(codewars上题目难度按等级排名,数字越小等级越高,题目也越难,1kyu代表1级即最难),仔细看了题目描述后发现和之前做过的一道4kyu的题类似,于是我就开始了解题之旅,最终在经历了2天的奋战后终于解决了这个问题。为了更好的巩固从问题中学习到的知识和思路,也为了做一次知识分享的尝试,我把从解题开始到结束的经历和感悟写成了这篇文章,初次尝试会比较生疏,希望大家能多多理解。

2、题目分析

题目链接

2.1 题目描述

In a grid of 7 by 7 squares you want to place a skyscraper in each square with only some clues:

  • The height of the skyscrapers is between 1 and 7
  • No two skyscrapers in a row or column may have the same number of floors
  • A clue is the number of skyscrapers that you can see in a row or column from the outside
  • Higher skyscrapers block the view of lower skyscrapers located behind them

Can you write a program that can solve this puzzle in time?

看到这或许你还不明白这道题想要你解决的问题到底是什么,那么请注意下面这句话:
This kata is based on 4 By 4 Skyscrapers and 6 By 6 Skyscrapers by FrankK. By now, examples should be superfluous; you should really solve Frank’s kata first, and then probably optimise some more. A naive solution that solved a 4×4 puzzle within 12 seconds might need time somewhere beyond the Heat Death of the Universe for this size. It’s quite bad.

里面对为什么题目描述中没有例子的原因做了解释:这道题是基于4x4和6x6的问题之上的,所以为了解决这个问题,我们需要先看一下4x4和6x6的题目。

2.2 4x4的问题

Example:

To understand how the puzzle works, this is an example of a row with 2 clues. Seen from the left side there are 4 buildings visible while seen from the right side only 1:
在这里插入图片描述
There is only one way in which the skyscrapers can be placed. From left-to-right all four buildings must be visible and no building may hide behind another building:
在这里插入图片描述
Example of a 4 by 4 puzzle with the solution:
在这里插入图片描述
看到这里应该大概了解题目想说明什么了吧,想象在每个格子里放置一个高度为格子中数字的’楼房’,然后从对应的方向看过去,能看到几个’楼房’的数量即为格子外的限制条件。
1234 这样一排数字,从左往右看,4个格子的’楼房’都能被看到,所以左边是4,而从右往左看的话因为最右边的格子里的4层高的’楼房’把后边的全部挡住了,所以只能看到1个’楼房’,右边就是1

下面我们再来题目需求

Task:

  • Finish:
func SolvePuzzle(clues []int) [][]int
  • Pass the clues in an array of 16 items. This array contains the clues around the clock, index:
    在这里插入图片描述
  • If no clue is available, add value 0
  • Each puzzle has only one possible solution
  • SolvePuzzle() returns matrix int[][]. The first indexer is for the row, the second indexer for the column. (Python: returns 4-tuple of 4-tuples, Ruby: 4-Array of 4-Arrays)

看到这题目需求就完全明确了,一开始会给定一个数组作为限制条件(即从各行各列各方向看过去的楼层高度),我们需要完成一个SolvePuzzle函数,函数的功能如下:

  • 计算一个行列满足给定限定条件的二维数组
  • 每行每列的数字不能重复
  • 需要在12秒内计算出结果

2.3 思路分析

  • 首先想到是8皇后的问题(如果不了解8皇后问题的自行百度喔),但又多了限制条件,于是重新思考思路。重新思考的思路还是基于8皇后之上,这种问题一般都需要使用回溯来解决,主要是找到回溯的条件。苦苦思考之后,发现这个问题的限制条件是一行或一列的,那么可以每次填写一整行和一整列,这样会减少回溯的次数,至于每次回溯的限制,则直接根据行或列两头的限制条件来决定。
  • 然后又出现了另外的问题,比如4x4两边的限制条件是1和4,那么就只有一种填法:4321,而且4x4的排列组合很少,完全可以手动把每种限制条件的排序全部列出来再选择。但7x7的问题如果要自己手动写的话就太多,还容易出错,于是就想到了先生成一个全排列的列表,然后遍历列表,把每一个排列从头到尾能数出的高度和从尾到头能数出的高度全部计算出来,同时按照高度分在不同的map中,这样方便后面的使用。
  • 各个排列对应的数字都计算好了,现在需要的是划分限制条件数组了,从4x4的问题中我们能够看出,给定了16个数字,但是其中0-311-84-712-15是一一对应限制了一列或一行的,那么可以将限制条件筛选出来。首先筛选从小到大筛选两头都有数字的限制条件为一组,因为两头都有限制的行或列对应可以填的数字组合越少,然后再把只有一边有数字的限制条件筛选出来,如果两边都为0的直接忽略。选好之后再按照限制条件的大小进行排序,组装成一个列表,确保限制条件越大的在前面。
  • 前提工作已经完成了,这个时候就需要开始填写格子了,首先明确的是,我们需要先填写有限制条件的格子,即每行每列两头有数字的格子,根据限制条件从前面计算的全排列组合高度map中取出组合填入格子中,填入的时候需要判断是否和已有的数字冲突,如果冲突则不符合,需要重新取组合,填好一个限制条件对应的行或列后再填下一个规则,若所有的组合都不能填入表格中,则应该回溯到上一步重新填写,知道所有的规则都填写完为止。
  • 不要以为这个时候就完成了,刚刚我们填写的只是有规则的行或列,还有些格子是没有被规则覆盖到的,这个时候我们就需要一个格子一个格子的填写,每个格子填写的时候需要判断行和列中的数字是否重复,如果不重复则填好后继续填下一个格子,如果所有数字都重复则需要回溯到上一个格子重新填写,如果无法完成空白格子的填写,则需要回溯到上一步规则填写中,从最后一个规则重新填写,直到所有的空白格子都完成填写,这个时候才最终完成了解题。

3、代码编写

完整的代码地址 : https://github.com/wshhz/codewars/blob/master/1kyu/solvePuzzle.go

3.1 全排列组合及统计

  • 先通过go轻量级的协程来生成全排列的列表
// PermutationConcurrency  并发计算全排列
func PermutationConcurrency(s []int) [][]int {
   
	req, out := make(chan []int), make(chan []int)

	//开启goroutine计算
	permutaionConImpl(req, out, s)

	over := make(chan [][]int)

	//要开goroutine读取out,如果放在主函数中,会导致死锁。
	go func() {
   
		result := make([][]int, 0)

		for res := range out {
   
			result = append(result, res)
		}

		over <- result
	}()

	for _, c := range s {
   
		sl := []int{
   c}
		req <- sl
	}
	close(req)

	return <-over
}

func prefixIncrement(in []int, s []int, next chan []int) {
   
	for _, c := range s {
   
		exist := false
		for _, e := range in {
   
			if e == c {
   
				exist = true
				break
			}
		}
		if exist {
   
			continue
		}

		temp := make([]int, 0)
		temp = append(temp, in
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值