程式碼隨想錄day 53 || 圖論4

周公瑾55發表於2024-09-07

字串接龍

image


var queue *list.List
var visitMap map[string]bool

func main() {
	var count int
	fmt.Scanf("%d", &count)
	var startStr, endStr string
	fmt.Scanf("%s %s", &startStr, &endStr)

	var strList = make([]string, count)
	for i := 0; i < count; i++ {
		var str string
		fmt.Scanf("%s", &str)
		strList[i] = str
	}
	fmt.Println(startStr, endStr, strList)

	queue = list.New()
	queue.PushBack(startStr)
	visitMap = make(map[string]bool, len(strList)) // 存放字元的使用狀態
	for _, v := range strList {
		visitMap[v] = false
	}

	bfs(endStr, strList)
}

func bfs(target string, strList []string) {
	// 本題的思路是構建無向圖,然後廣搜得到最短路徑
	// 但是具體怎麼實現?先構建再廣搜?並不是,是一邊廣搜一邊半構建
	// 具體過程是,從startStr出發,對於每一個字元,嘗試使用26個字母替換,如果替換後的新字串出現在strlist中,那麼加入佇列,路徑+1,直到最終找到endStr
	
	// todo: 廣搜如何記錄層數,也就是路徑深度,應該是每一層都標記一下入隊長度,如果該層完全出隊,層度+1,太長就不寫了

	for queue.Len() > 0 {
		node := queue.Remove(queue.Front()).(string)
		nodeRune := []rune(node)
		for idx, _ := range nodeRune {
			for i := 0; i < 26; i++ {
				newStr := nodeRune
				newStr[idx] = rune(i + 'a')

				// 如果直接變成target,返回
				if string(newStr) == target {
					count++
					return
				}

				// 遇到字典元素, 並且沒有使用過
				if v, ok := visitMap[string(newStr)]; ok && !v {
					visitMap[string(newStr)] = true
					queue.PushBack(string(newStr))
				}
			}

		}
	}

}


有向圖完全可達性

package main

import (
	"container/list"
	"fmt"
)

var queue *list.List
var visited []bool

func main() {
	//var c, l int
	//fmt.Scanf("%d %d", &c, &l)
	//
	//// 構建鄰接矩陣儲存圖
	//var graph = make([][]int, c)
	//for i, _ := range graph {
	//	graph[i] = make([]int, c)
	//}
	//for i := 0; i < l; i++ {
	//	var x, y int
	//	fmt.Scanf("%d %d", &x, &y)
	//	graph[x-1][y-1] = 1
	//}
	//for _, v := range graph {
	//	fmt.Println(v)
	//}

	c := 4
	//l := 4
	graph := [][]int{{0, 1, 1, 0}, {1, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}}

	// dfs
	// 本題原理就是找到首個節點到所有節點是否路徑可達,所以dfs判斷兩個節點是否存在路徑(路徑可達)
	for i := 1; i < c; i++ {
		visited = make([]bool, c)
		if dfs(0, i, graph) == false { // 任意一個節點不可達,就是false
			fmt.Printf("dfs %d false\n", i)
			break
		}
	}

	// bfs
	for i := 1; i < c; i++ {
		visited = make([]bool, c)
		visited[0] = true
		queue = list.New()
		queue.PushBack(0)
		if bfs(graph, i) == false {
			fmt.Printf("bfs %d false\n", i)
			break
		}
	}
	fmt.Println("true")
}

func dfs(start, stop int, graph [][]int) bool {
	// 遞迴終止條件,空,或者找到了target
	visited[start] = true // 標記節點已經遍歷過
	if start == stop {
		return true
	}

	for idx, v := range graph[start] {
		if v == 1 && !visited[idx] {
			res := dfs(idx, stop, graph)
			if res { // 某次遞迴出現了目標,直接返回true
				return true
			}
		}
	}

	// 回溯
	visited[start] = false
	return false
}

func bfs(graph [][]int, target int) bool {
	for queue.Len() > 0 {
		node := queue.Remove(queue.Front()).(int)
		if node == target {
			return true
		}
		for i, v := range graph[node] {
			if v == 1 && !visited[i] {
				visited[i] = true
				queue.PushBack(i)
			}
		}
	}

	return false
}


463 島嶼周長

var dirPath = [4][2]int{{0,1}, {0,-1}, {1,0}, {-1,0}}
var sum int // 周長
var visited [][]bool
func islandPerimeter(grid [][]int) int {
	// 很簡單的思路,計算所有的陸地,然後區分不同的周長
	// 三面環水,周長為3,兩面,周長是2... 四周幾格水周長就是幾,邊界也算作水
	sum = 0
	visited = make([][]bool, len(grid))
	for i, _ := range visited {
		visited[i] = make([]bool, len(grid[0]))
	}

	for i:=0; i<len(grid); i++ {
		for j:=0; j<len(grid[0]); j++ {
			if grid[i][j] == 1 && !visited[i][j] {
				dfs(i, j, grid)
			}
		}
	}
	return sum
}

func dfs(x, y int, graph [][]int) {

	if graph[x][y] == 0 || visited[x][y] {
		visited[x][y] = true
		return
	}
	visited[x][y] = true

	for _, dir := range dirPath {
		next_x, next_y := x + dir[0], y+ dir[1]
		if next_x < 0 || next_y<0 || next_x >= len(graph) || next_y >= len(graph[0]) {
			sum += 1  // 當前陸地的周邊是邊界,視為水,周長+1
			continue
		}
		if graph[next_x][next_y] == 0 { // 當前陸地的周邊是水,周長+1
			sum += 1
		}
		if graph[next_x][next_y] == 1 && !visited[next_x][next_y] {
			// 是新大陸
			dfs(next_x, next_y, graph)
		}
	}
}

相關文章