Interactive Problems
Interactive problems were already present in IOI last century. Although interactive problems have not appeared in provincial-level and below competitions in recent years, the NOI series in 2019 featured two interactive problems in a row: "P5208 [WC2019] I Jun's Shop" and "P5473 [NOI2019] I Jun's Adventure", which may indicate the return of interactive problems to the NOI series.
Interactive problems do not require high-level algorithmic prerequisites and generally have no strict time limits; the quality of a program often depends only on the interaction count limit. Therefore, when learning interactive problems, it is recommended to progress step by step in order of difficulty. If you intend to exercise algorithmic thinking rather than merely learning algorithms, solving interactive problems is an excellent method. Although interactive problems usually have lower requirements for algorithms already mastered by contestants, it is still recommended to have a certain level of advanced and provincial-level algorithmic knowledge before attempting interactive problems, as your algorithmic thinking and knowledge base will have reached a certain level by then. For a basic introduction to interactive problems, refer to OI Wiki's Problem Types - Interactive Problems.
Special errors in interactive problems:
- Contestants must flush the buffer after each output; otherwise, an "Idleness limit exceeded" error will occur. Additionally, if the problem contains multiple test cases and the program can determine the answer before reading all data, it must still read all data; otherwise, an ILE will also occur due to input confusion (you can submit multiple queries at once and receive all answers at once). Also, try to avoid using fast input.
- If the program makes too many queries, Codeforces will give a "Wrong Answer" verdict (though the judging system will explain the reason for the Wrong Answer), while UVa will give a "Protocol Limit Exceeded (PLE)" verdict.
- If the program's interaction format is incorrect, UVa will give a "Protocol Violation (PV)" verdict.
Due to the cumbersome input and output in interactive problems, it is recommended to encapsulate input and output functions separately.
During competitions, if the problem author provides a grader header file (for debugging grader interactive problems) or a checker program (for debugging stdio interactive problems), debugging interactive problems becomes relatively simple, because testing interactive problems is much more difficult than testing ordinary problems. Without testlib.h, the stdio interaction library for problems with many interaction details typically has about 3k lines of code, plus a 3k-line testing program, requiring at least an hour to implement. However, regardless of whether debugging tools are available, debugging interactive problem code often requires contestants to simulate the interaction process with the program. Therefore, interactive problems require contestants to design high-quality programs, striving to get them right on the first try, while also possessing strong static error-checking abilities.
Example problems:
- CF679A Bear and Prime 100
- CF843B Interactive LowerBound
- UOJ206 [APIO2016] Gap
- CF750F New Year and Finding Roots
- UVa12731 Mysterious Space Station
CF679A Bear and Prime 100
Each prime number has exactly two factors, so we can directly enumerate the factors of the number to guess. Since the limit is at most 20 queries, and for larger numbers (e.g., 92), attempting prime factorization reveals that we need to enumerate primes up to \(\lfloor\frac{n}{2}\rfloor\). Therefore, we first sieve primes within 50 and query all these numbers each time.
Since testing for this problem is relatively easy, we can try all numbers within the range. We will find that the program cannot effectively handle squares of primes. So we include the squares of 2, 3, 5, and 7: 4, 9, 25, 49, totaling 19 numbers, which meets the requirement.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
CF843B Interactive LowerBound
The linked list has at most \(5 \times 10 ^ 4\) elements, but we can only query \(1999\) times and can only obtain the next element of a node, so the ordinary method of traversing the entire list is not feasible. The only way to approximate the target element's position is to randomly sample points.
For \(n < 2000\), we can directly enumerate. For \(n \ge 2000\), we randomly sample 1000 points. The expected distance between these points is small, so we can start traversing from the largest value less than \(x\). It can be proven that we will obtain the answer before reaching the next sampled point. During traversal, once we find an element greater than or equal to \(x\), we can directly output it.
Although the overall idea is simple, in practice, without knowledge of non-perfect random algorithms like simulated annealing, thinking about it might be more difficult.
Additionally, due to Codeforces' hack mechanism, many people deliberately target code without initialized random seeds, so we need srand((size_t)new char) before random_shuffle().
Reference Code
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 | |
UOJ206 [APIO2016] Gap
Discuss two subtasks:
-
Query count limit.
Consider the first query. Since we initially know nothing, we need to query the range \([1, 10 ^ {18}]\) to obtain the minimum and maximum values.
Since the query limit is exactly \(\frac{N + 1}{2}\), consider how to obtain previously unacquired values each time, so as to acquire all numbers in the sequence within the limit. The method is simple: after each query \([s, t]\), let the obtained values be \(mn, mx\); then the next query is \([mn + 1, mx - 1]\).
-
Query interval size limit.
Since the problem requires that the total number of numbers in queried intervals does not exceed \(3N\), consider minimizing the query intervals. The above method is no longer usable because the total number of numbers in queried intervals scales as \(O(N ^ 2)\). We can consider binary search on the value range, but this method is not reliable, as the worst case might be \(O(N ^ 2)\). So we need a more efficient way to partition the value range, avoiding repeated queries of points within query intervals and wasting opportunities.
Considering that the answer will not be less than \(\lfloor\frac{a_n - a_1}{N - 1}\rfloor\), we can partition the value range by this value. Let \(i\) start at 0, \(ans\) start at the above value. Each time query \([i, i + ans]\) and update \(ans\), then increment \(i\) by \(ans\).
However, this method also does not work well for subtask 1, because in the worst case many queries might contain no numbers in their value range.
Reference Code
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 | |
CF750F New Year and Finding Roots
Given the strict requirements \(h \le 7\) and query count \(\le 16\), we need to maximize the use of information obtained from queries very strictly.
For \(h \le 4\), we can directly brute force. However, for \(h > 4\), we need an efficient traversal algorithm.
Random sampling is not a good method because we cannot determine if we are close enough to the root, and even excluding repeated sampling, the probability of hitting the root at least once is \(1 - (\frac{2 ^ h - 2}{2 ^ h - 1})\), which is still very small.
Since \(1 \le k \le 3\), and we do not know which direction is closer to the root, we consider the worst case: if \(k = 3\), the first two traversal directions are away from the root, and the third is toward the root. So we must traverse in all three directions.
Consider both bfs and dfs traversal methods. Since the bfs search tree might be large, we prioritize dfs. Of course, if we know the current depth and the search tree size within the depth range is less than or equal to the remaining queries, we can directly use bfs.
Knowing the current node's depth and the current traversal direction provides a significant advantage. However, knowing whether we are moving toward the root or toward a leaf is very difficult. When using dfs, we only know the direction when we reach the root (\(k = 2\)) or a leaf (\(k = 1\)). So we need to know the current node's depth as much as possible and cannot use methods like iterative deepening search, stopping midway.
Consider randomly selecting an initial node; starting from it might encounter the worst case described above.
If \(k = 1\), we directly know the current node's depth.
If \(k = 2\), the current node is the root.
If \(k = 3\), we directly dfs in three directions. Considering that two directions are directly toward leaves, the traversal path lengths are the same; the other direction is toward the root, but we might accidentally go toward a leaf, resulting in a longer traversal path. At this point, we can calculate the current node's depth.
When \(k = 1\) or \(k = 3\), we need to consider longer traversal paths. We can find the node with the minimum depth on the path (which must be shallower than the initial node). If we mark visited nodes and avoid revisiting, only one traversal path remains from that node. Although this path might still lead to a leaf, it also necessarily contains a node shallower than the starting point, from which we can repeat the above steps.
Of course, considering the worst case for \(h = 7\) (each step toward the root is immediately followed by a step toward a leaf), we find that if we only use dfs, we need at worst \(\frac{(1 + 7) \times 7}{2} = 28\) queries. However, we already know the initial node's depth, so we can calculate the depths of all visited nodes and, based on our earlier discussion about bfs, decide whether we can directly bfs from the shallowest node.
At this point, we can calculate that at worst we need 17 queries. So we consider removing one node from the search tree (due to dfs's blind traversal nature, we consider bfs): when performing bfs of depth \(k\), the search tree has at worst \(2 ^ k - 1\) nodes, possibly requiring \(2 ^ k - 1\) queries to determine which node has exactly 2 neighbors. However, if we have already queried \(2 ^ k - 2\) of them, we know the last node must be the root.
The optimal solution in the worst case is: for \(h = 7\), start dfs from a leaf, each time taking only one step toward the root before immediately going toward a leaf. After 10 queries, the current known shallowest node depth is 4. Since its parent is known, directly start bfs from its parent (search tree depth 3, node count \(2 ^ 3 - 1 = 7\)). During bfs, after querying \(2 ^ 3 - 2 = 6\) times, determine that the last node in the bfs search tree is the root.
Our algorithm can exactly hit the worst-case limit of 16 queries.
Reference Code
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 | |
UVa12731 Mysterious Space Station
Since the only feedback is whether moving causes a collision, we should consider walking as close to walls as possible without getting lost. This has several advantages:
- When walking near walls, it is easy to know if we will collide, obtaining as much information as possible.
- Cells adjacent to walls are not teleporter cells, avoiding the robot getting lost.
So, if we know the robot might be at a certain position near a wall, to confirm whether the robot is indeed there, we can use the "wall-following" method to determine if we are really at that position. According to topology principles, in a maze with walls on both sides, if we enter from the entrance and always keep one hand on the same wall, we can guarantee finding the exit. Since the walls in this problem are closed, simply following the path along the walls ensures we can return to the starting point without colliding. Additionally, since the wall-adjacent path is the largest closed loop on the map, we don't need to deliberately collide to keep the robot near walls in the code; we can mark the wall-adjacent path on the map. Moreover, once we collide, we need to quickly backtrack along the original path, reducing steps while avoiding getting lost.
From the above, we can infer a trial-and-error method to determine if the robot is at a specific cell: move the robot to the wall-adjacent path without stepping into unknown cells or known teleporters, then walk around the wall-adjacent path once. If no collision occurs during this process, we can confirm the robot is indeed at the specific cell.
We can use the above method: initially mark all unknown cells on the map, then check each unknown cell from top to bottom, left to right to see if it is a teleporter. First move above the unknown cell, then move down and left. Then use the above method to check if the robot is to the left of the unknown cell. If not, the robot is not where it should be, meaning the unknown cell is a teleporter.
After identifying unknown cells, we need to determine the pairing relationships among the 2k unknown cells. The actual method is simple: just brute-force pairing. Since \(k \le 5\), at most \(9 + 7 + 5 + 3\) trial-and-error attempts are needed. In contrast, checking all unknown cells on the map requires at most \(121 - 40\) trial-and-error attempts.
Currently, the following code can only pass the UOJ mirror problem: #247. "Rujia Liu's Present 7" Mysterious Space Station, but not the original UVa problem. After modifying Liu Rujia's reference solution on UOJ, it still cannot pass, and we cannot contact Liu Rujia for now. Therefore, the following code is based on UOJ.
However, Liu Rujia's reference solution is of much higher quality than the code below. You can view the reference solution that passed the UOJ mirror problem. On the same data, the reference solution uses very few moves.
Reference Code
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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
Exercises
- Liu Rujia's interactive problem contest "Rujia Liu's Present 7" is of very high quality and highly recommended.
- P5473 [NOI2019] I Jun's Adventure
- P5208 [WC2019] I Jun's Shop
References and Further Reading
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用