-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLab10.cs
More file actions
168 lines (138 loc) · 7.02 KB
/
Copy pathLab10.cs
File metadata and controls
168 lines (138 loc) · 7.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using ASD.Graphs;
using System;
using System.Collections.Generic;
namespace ASD
{
public class Lab10 : MarshalByRefObject
{
/// <param name="labyrinth">Graf reprezentujący labirynt</param>
/// <param name="startingTorches">Ilość pochodni z jaką startują bohaterowie</param>
/// <param name="roomTorches">Ilość pochodni w poszczególnych pokojach</param>
/// <param name="debt">Ilość złota jaką bohaterowie muszą zebrać</param>
/// <param name="roomGold">Ilość złota w poszczególnych pokojach</param>
/// <returns>
/// Informację czy istnieje droga przez labirynt oraz tablicę reprezentującą kolejne wierzchołki na drodze.
/// W przypadku, gdy zwracany jest false, wartość tego pola powinna być null.
/// </returns>
public (bool routeExists, int[] route) FindEscape(Graph labyrinth, int startingTorches, int[] roomTorches,
int debt, int[] roomGold)
{
int n = labyrinth.VertexCount;
// Wartości początkowe
var S = new List<int> { 0 };
int curTorches = startingTorches + roomTorches[0];
int curDebt = debt - roomGold[0];
bool[] visited = new bool[n];
visited[0] = true;
bool FindPath(int last)
{
if (last == n - 1)
return curDebt <= 0;
if (curTorches == 0)
return false;
foreach (int u in labyrinth.OutNeighbors(last))
{
// W tym etapie możemy wejść do każdego wierzchołka tylko raz
if (visited[u])
continue;
S.Add(u);
visited[u] = true;
curTorches += roomTorches[u] - 1;
curDebt -= roomGold[u];
if (FindPath(u))
return true;
visited[u] = false;
curTorches -= roomTorches[u] - 1; // -(-1) = +1
curDebt += roomGold[u];
S.RemoveAt(S.Count - 1);
}
return false;
}
bool routeExists = FindPath(0);
return (routeExists, routeExists ? S.ToArray() : null);
}
/// <param name="labyrinth">Graf reprezentujący labirynt</param>
/// <param name="startingTorches">Ilość pochodni z jaką startują bohaterowie</param>
/// <param name="roomTorches">Ilość pochodni w poszczególnych pokojach</param>
/// <param name="debt">Ilość złota jaką bohaterowie muszą zebrać</param>
/// <param name="roomGold">Ilość złota w poszczególnych pokojach</param>
/// <param name="dragonDelay">Opóźnienie z jakim wystartuje smok</param>
/// <returns>
/// Informację czy istnieje droga przez labirynt oraz tablicę reprezentującą kolejne wierzchołki na drodze.
/// W przypadku, gdy zwracany jest false, wartość tego pola powinna być null.
/// </returns>
public (bool routeExists, int[] route) FindEscapeWithHeadstart(Graph labyrinth, int startingTorches,
int[] roomTorches, int debt, int[] roomGold, int dragonDelay)
{
int n = labyrinth.VertexCount;
// Wartości początkowe
var S = new List<int> { 0 };
int destTime = 1; // Zaczniemy od 1, żeby domyślne 0 było jako nieodwiedzone
int curDebt = debt - roomGold[0];
int curTorches = startingTorches + roomTorches[0];
// (czas w którym odwiedzono wierzchołek, dług wtedy, liczba pochodni wtedy)
var visited = new (int, int, int)[n]; // domyślnie (0, 0, 0)
visited[0] = (destTime, curDebt, curTorches);
bool FindPath(int last)
{
if (last == n - 1)
return curDebt <= 0;
if (curTorches == 0)
return false;
destTime++;
curTorches--;
foreach (int u in labyrinth.OutNeighbors(last))
{
(int visitedAt, int debtThen, int torchesThen) = visited[u];
int lastDelay = dragonDelay;
int lastDestTime = destTime;
// Czy już tutaj byliśmy
if (visitedAt > 0)
{
// Smok może iść tylko po naszych śladach, nie znajdzie ścieżki złożonej z innych krawędzi.
// Zatem może nas dogonić tylko pomijając cykle.
// Sprawdzamy, czy ta różnica jest większa od delay'a, w takim przypadku znaczy to, że smok
// zdążył dostać się do tamtego wierzchołka i go zniszczyć (delay będzie się zmniejszał
// o te zamykane cykle). (nierówność ostra, bo smok jeszcze traci jeden ruch na początku)
if (destTime - visitedAt > dragonDelay)
continue;
// Nie ma sensu wchodzić z powrotem, jeżeli nie zebraliśmy więcej złota
// i nie zdobyliśmy dodatkowych pochodni. (Eliminujemy zbędne kręcenie się w kółko)
if (curDebt >= debtThen && curTorches <= torchesThen)
continue;
// Pomniejszamy delay o różnicę czasu i czasu wejścia, bo w tym momencie zrobiliśmy jakiś cykl,
// którego długość jest tą różnicą.
dragonDelay -= destTime - visitedAt;
// W tym przypadku też trzeba zmniejszyć destTime o długość tego cyklu (ustawić go na visitedAt),
// bo zamykając ten cykl, zmniejszyliśmy wyżej dragonDelay i jakby zapomniamy o nim.
destTime = visitedAt;
}
S.Add(u);
// Zbieramy przedmioty, jeżeli jesteśmy tutaj za pierwszym razem
if (visitedAt == 0)
{
curDebt -= roomGold[u];
curTorches += roomTorches[u];
}
visited[u] = (destTime, curDebt, curTorches);
if (FindPath(u))
return true;
if (visitedAt == 0)
{
curDebt += roomGold[u];
curTorches -= roomTorches[u];
}
visited[u] = (visitedAt, debtThen, torchesThen);
dragonDelay = lastDelay;
destTime = lastDestTime;
S.RemoveAt(S.Count - 1);
}
destTime--;
curTorches++;
return false;
}
bool routeExists = FindPath(0);
return (routeExists, routeExists ? S.ToArray() : null);
}
}
}