Skip to content

Commit dc4dcf8

Browse files
committed
Updated utils, graph, &polyfence. Added xor & minpresses
1 parent ce7b5b0 commit dc4dcf8

File tree

5 files changed

+450
-4
lines changed

5 files changed

+450
-4
lines changed

utils/graph/graph.go

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package graph
22

3-
import "image"
3+
import (
4+
"container/heap"
5+
"image"
6+
"math"
7+
)
48

59
type Queue[T any] []T
610

@@ -92,3 +96,170 @@ func Search(g Graph, s, e image.Point) (res []image.Point) {
9296
}
9397
return
9498
}
99+
100+
// PriorityQueueItem represents an item in the priority queue
101+
type PriorityQueueItem struct {
102+
Node string
103+
Distance int
104+
Index int
105+
}
106+
107+
// PriorityQueue implements heap.Interface for Dijkstra's algorithm
108+
type PriorityQueue []*PriorityQueueItem
109+
110+
func (pq PriorityQueue) Len() int { return len(pq) }
111+
112+
func (pq PriorityQueue) Less(i, j int) bool {
113+
return pq[i].Distance < pq[j].Distance
114+
}
115+
116+
func (pq PriorityQueue) Swap(i, j int) {
117+
pq[i], pq[j] = pq[j], pq[i]
118+
pq[i].Index = i
119+
pq[j].Index = j
120+
}
121+
122+
func (pq *PriorityQueue) Push(x interface{}) {
123+
n := len(*pq)
124+
item := x.(*PriorityQueueItem)
125+
item.Index = n
126+
*pq = append(*pq, item)
127+
}
128+
129+
func (pq *PriorityQueue) Pop() interface{} {
130+
old := *pq
131+
n := len(old)
132+
item := old[n-1]
133+
old[n-1] = nil
134+
item.Index = -1
135+
*pq = old[0 : n-1]
136+
return item
137+
}
138+
139+
// Dijkstra finds the shortest path from start to end node
140+
// graph is a map of node -> list of connected nodes
141+
// Returns the distance to the end node, or -1 if no path exists
142+
func Dijkstra(graph map[string][]string, start, end string) int {
143+
distances := make(map[string]int)
144+
145+
// Initialize distances for all nodes in the graph
146+
for node := range graph {
147+
distances[node] = math.MaxInt32
148+
}
149+
distances[start] = 0
150+
151+
// Also ensure end node exists in distances even if it has no outgoing edges
152+
if _, exists := distances[end]; !exists {
153+
distances[end] = math.MaxInt32
154+
}
155+
156+
pq := make(PriorityQueue, 0)
157+
heap.Init(&pq)
158+
heap.Push(&pq, &PriorityQueueItem{
159+
Node: start,
160+
Distance: 0,
161+
})
162+
163+
visited := make(map[string]bool)
164+
165+
for pq.Len() > 0 {
166+
current := heap.Pop(&pq).(*PriorityQueueItem)
167+
168+
if current.Node == end {
169+
return current.Distance
170+
}
171+
172+
if visited[current.Node] {
173+
continue
174+
}
175+
visited[current.Node] = true
176+
177+
for _, neighbor := range graph[current.Node] {
178+
if visited[neighbor] {
179+
continue
180+
}
181+
182+
newDist := current.Distance + 1 // Each edge has weight 1
183+
184+
if newDist < distances[neighbor] {
185+
distances[neighbor] = newDist
186+
heap.Push(&pq, &PriorityQueueItem{
187+
Node: neighbor,
188+
Distance: newDist,
189+
})
190+
}
191+
}
192+
}
193+
194+
// No path found
195+
return -1
196+
}
197+
198+
// DijkstraWithPath finds the shortest path and returns both distance and the path
199+
func DijkstraWithPath(graph map[string][]string, start, end string) (int, []string) {
200+
distances := make(map[string]int)
201+
previous := make(map[string]string)
202+
203+
// Initialize distances for all nodes in the graph
204+
for node := range graph {
205+
distances[node] = math.MaxInt32
206+
}
207+
distances[start] = 0
208+
209+
// Also ensure end node exists in distances even if it has no outgoing edges
210+
if _, exists := distances[end]; !exists {
211+
distances[end] = math.MaxInt32
212+
}
213+
214+
pq := make(PriorityQueue, 0)
215+
heap.Init(&pq)
216+
heap.Push(&pq, &PriorityQueueItem{
217+
Node: start,
218+
Distance: 0,
219+
})
220+
221+
visited := make(map[string]bool)
222+
223+
for pq.Len() > 0 {
224+
current := heap.Pop(&pq).(*PriorityQueueItem)
225+
226+
if current.Node == end {
227+
// Reconstruct path
228+
path := []string{}
229+
node := end
230+
for node != "" {
231+
path = append([]string{node}, path...)
232+
prev, exists := previous[node]
233+
if !exists {
234+
break
235+
}
236+
node = prev
237+
}
238+
return current.Distance, path
239+
}
240+
241+
if visited[current.Node] {
242+
continue
243+
}
244+
visited[current.Node] = true
245+
246+
for _, neighbor := range graph[current.Node] {
247+
if visited[neighbor] {
248+
continue
249+
}
250+
251+
newDist := current.Distance + 1
252+
253+
if newDist < distances[neighbor] {
254+
distances[neighbor] = newDist
255+
previous[neighbor] = current.Node
256+
heap.Push(&pq, &PriorityQueueItem{
257+
Node: neighbor,
258+
Distance: newDist,
259+
})
260+
}
261+
}
262+
}
263+
264+
return -1, nil
265+
}

utils/minpresses/minpresses.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package minpresses
2+
3+
import "sort"
4+
5+
// SolveMinPresses finds the minimum number of button presses to reach target joltages
6+
func SolveMinPresses(buttonPositions [][]int, targetJoltages []int) (int, bool) {
7+
numButtons := len(buttonPositions)
8+
numPositions := len(targetJoltages)
9+
10+
// Sort buttons by number of positions affected
11+
type ButtonInfo struct {
12+
positions []int
13+
}
14+
15+
buttons := make([]ButtonInfo, numButtons)
16+
for i := 0; i < numButtons; i++ {
17+
buttons[i] = ButtonInfo{positions: buttonPositions[i]}
18+
}
19+
20+
sort.Slice(buttons, func(i, j int) bool {
21+
return len(buttons[i].positions) < len(buttons[j].positions)
22+
})
23+
24+
// Use a fixed-size state for faster hashing (max 10 positions should be enough)
25+
type State struct {
26+
btnIdx int
27+
vals [10]int
28+
}
29+
30+
memo := make(map[State]int)
31+
32+
var dp func(btnIdx int, current []int) int
33+
dp = func(btnIdx int, current []int) int {
34+
if btnIdx >= numButtons {
35+
for i := 0; i < numPositions; i++ {
36+
if current[i] != targetJoltages[i] {
37+
return 1e9
38+
}
39+
}
40+
return 0
41+
}
42+
43+
// Create state
44+
var state State
45+
state.btnIdx = btnIdx
46+
copy(state.vals[:], current)
47+
48+
if cached, ok := memo[state]; ok {
49+
return cached
50+
}
51+
52+
btn := buttons[btnIdx]
53+
54+
// Calculate max needed
55+
maxNeeded := 0
56+
for _, pos := range btn.positions {
57+
if pos < numPositions {
58+
need := targetJoltages[pos] - current[pos]
59+
if need > maxNeeded {
60+
maxNeeded = need
61+
}
62+
}
63+
}
64+
65+
minResult := int(1e9)
66+
67+
for presses := 0; presses <= maxNeeded; presses++ {
68+
newCurrent := make([]int, numPositions)
69+
copy(newCurrent, current)
70+
valid := true
71+
72+
for _, pos := range btn.positions {
73+
if pos < numPositions {
74+
newCurrent[pos] += presses
75+
if newCurrent[pos] > targetJoltages[pos] {
76+
valid = false
77+
break
78+
}
79+
}
80+
}
81+
82+
if valid {
83+
result := dp(btnIdx+1, newCurrent)
84+
if result < 1e9 {
85+
total := presses + result
86+
if total < minResult {
87+
minResult = total
88+
}
89+
}
90+
}
91+
}
92+
93+
memo[state] = minResult
94+
return minResult
95+
}
96+
97+
initial := make([]int, numPositions)
98+
result := dp(0, initial)
99+
100+
if result >= 1e9 {
101+
return 0, false
102+
}
103+
return result, true
104+
}

utils/polyfence/polyfence.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,50 @@ func (p Polygon) Copy() Polygon {
2626
}
2727

2828
func (pf *Polyfence) Inside(P image.Point) bool {
29+
// First check if point is on an edge
30+
if pf.OnEdge(P) {
31+
return true
32+
}
2933
return !(checkOutside(pf.p, P) == 0)
3034
}
3135

36+
func (pf *Polyfence) OnEdge(P image.Point) bool {
37+
n := len(pf.p) - 1
38+
for i := 0; i < n; i++ {
39+
p1, p2 := pf.p[i], pf.p[i+1]
40+
41+
// Check if P is on the line segment from p1 to p2
42+
// Point is on segment if it's collinear and between p1 and p2
43+
44+
// Check if collinear using cross product
45+
cross := (p2.X-p1.X)*(P.Y-p1.Y) - (P.X-p1.X)*(p2.Y-p1.Y)
46+
if cross != 0 {
47+
continue
48+
}
49+
50+
// Check if P is between p1 and p2
51+
minX, maxX := p1.X, p2.X
52+
if minX > maxX {
53+
minX, maxX = maxX, minX
54+
}
55+
minY, maxY := p1.Y, p2.Y
56+
if minY > maxY {
57+
minY, maxY = maxY, minY
58+
}
59+
60+
if P.X >= minX && P.X <= maxX && P.Y >= minY && P.Y <= maxY {
61+
return true
62+
}
63+
}
64+
return false
65+
}
66+
3267
func checkOutside(p Polygon, P image.Point) (res int) {
3368
if len(p) < 3 {
3469
return 0
3570
}
36-
edges := len(p) - 2
37-
for i := 0; i <= edges; i++ {
71+
n := len(p) - 1 // Since we append first point to close, check all edges
72+
for i := 0; i < n; i++ {
3873
if p[i].Y <= P.Y {
3974
if p[i+1].Y > P.Y {
4075
if isLeft(p[i], p[i+1], P) > 0 {

utils/utils.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ func Abs[T Number](i T) T {
6969
}
7070

7171
func IntPow(n, m int) int {
72-
if m == 0 || n == 0 {
72+
if m == 0 {
73+
return 1
74+
}
75+
if n == 0 {
7376
return 0
7477
}
7578
if m == 1 {

0 commit comments

Comments
 (0)