Bot pour Tetris et animation d'ingénierie inverse. Analyse de la piste mobile du deuxième championnat de programmation


Gagnants de la piste mobile

Au cours de la dernière année, Yandex a organisé deux championnats de programmation en ligne. Nous continuons à publier des analyses des tâches du deuxième championnat. Nous vous rappelons que les participants se sont vu proposer quatre pistes: machine learning, frontend, développement mobile et backend. Voici une discussion sur le tour de qualification pour les développeurs mobiles - 121 de ceux qui ont réussi les qualifications ont ensuite participé à la finale. Les tâches ont été inventées par des spécialistes de Yandex.Browser et d'autres équipes du portail de recherche. Dans ce hub, nous aborderons le sujet des algorithmes génétiques, rédigerons un bot pour Tetris et poursuivrons la quête avec différentes méthodes pour Android et iOS.

A. Câbles

Publié par: Sergey Myasnikov
Toutes les languesPython 3.7.3 et Python 2.7
Limite de temps4 s20 s
Limite de mémoire256 Mo
Entrerentrée standard ou input.txt
Conclusionsortie standard ou output.txt
N ordinateurs ont été apportés au nouveau bureau. Le radiotélescope à proximité ne permet pas d'utiliser le Wi-Fi pour la communication et le service d'assistance devra organiser un réseau à l'aide de câbles LAN. Après avoir rassemblé tous les câbles qui traînent dans les coins, les administrateurs ont réussi à connecter K paires d'ordinateurs. Fatigué après une longue journée de travail, le personnel du helpdesk s'est tourné vers vous pour vous aider à calculer le budget nécessaire pour terminer la tâche. Vous disposez donc d'une liste de M paires d'ordinateurs pouvant connecter des administrateurs et du coût de chaque câble. Au début du travail, vous avez trouvé un bogue dans le système de calcul des coûts - au lieu d'ajouter les coûts, ils se multiplient - donc un câble avec une valeur de 4 et un câble avec une valeur de 3 ensemble ne sont pas 7, comme la logique le suggère, mais 12.

Pour terminer la tâche, vous devez calculer le coût des câbles suffisants pour l'organisation d'un réseau connecté - de telle sorte que chaque ordinateur est connecté à chacun directement ou indirectement.

Formats d'E / S, exemples et notes

Format d'entrée


La première ligne contient les nombres 0 <N <10 6 , 0 ≤ K <10 6 , 0 ≤ M <10 6 .

Les K lignes suivantes contiennent des paires de nombres 0 <u, v ≤ N - le nombre d'ordinateurs déjà connectés par le service d'assistance.

Les M lignes suivantes contiennent des triplets de nombres 0 <u, v ≤ N et 0 <p ≤ 10 9 - le nombre d'ordinateurs que les administrateurs peuvent connecter en utilisant un coût de câble p.

Format de sortie


Imprimez le coût minimum de câbles suffisant pour construire un réseau connecté, ou –1 s'il est impossible de construire un réseau complet.

La réponse peut être très large, vous devez donc la sortir modulo 2 31 - 1.

Exemple 1

EntrerConclusion
2 0 1
2 1 9
9

Exemple 2

EntrerConclusion
5 0 6
1 3 1
2 4 8
1 4 1
1 2 10
4 1 10
5 3 4
32

Exemple 3

EntrerConclusion
6 0 3
4 3 2
2 6 9
1 4 8
–1

Remarques


La quantité de données d'entrée peut atteindre 30 Mo - faites attention à la vitesse de lecture.

Solution


Le réseau complet qui doit être construit pour résoudre le problème n'est rien de plus qu'un arbre couvrant minimal. (Étant donné que K arêtes ont déjà été ajoutées au début de la construction, le graphique résultant n'est pas nécessairement un arbre, mais il est garanti de le contenir.)

Dans le cadre classique, le problème de la recherche de l'arbre couvrant minimum implique de minimiser la somme des bords, mais ici il est nécessaire de minimiser le produit. Heureusement, ces deux arbres coïncident. Ceci est facile à prouver en remplaçant les poids des bords par leurs logarithmes.

Les algorithmes les plus connus pour construire un arbre couvrant minimum sont les algorithmes Prima et Kraskal.

Dans le cas de graphes denses, l'algorithme de Prym a l'asymptotique O (N 2 ) qui, compte tenu de la restriction N <10 6 , ne permet pas de l'utiliser pour une solution complète du problème. (Mais notez que les données du test ont été compilées de sorte que la solution avec les asymptotiques indiquées ait marqué exactement la moitié du point)

Une implémentation naïve de l'algorithme Kruskal qui utilise un tableau pour conserver des informations sur la composition actuelle des composants connectés au graphe a l'asymptotique O (M log M + N 2 ). Ici, il est équivalent à O (N 2 ). Pour une solution complète, vous devez utiliser une structure de données connue sous le nom de système d'union disjointe (DSU).

DSU vous permet d'effectuer deux opérations: find (x) trouve le «leader» de l'ensemble auquel appartient x (le leader est traduit par injection en numéro de l'ensemble); union (x, y) combine les ensembles auxquels appartiennent x et y.

L'heuristique de compression de chemin et l'heuristique de combinaison de rang, généralement utilisées dans la mise en œuvre d'une structure, permettent d'obtenir un comportement asymptotique, dans des cas pratiques équivalents à O (1).

Supposons que nous ayons utilisé DSU pour conserver des informations sur les composants de connectivité du graphe pendant l'exécution de l'algorithme Kraskal. Le comportement asymptotique de l'algorithme sera alors O (M log M + N) = O (M log M).

Code de décision
 public class CablesSolution { private static final long MODULLO = 2147483647L; public static void main(String[] args) { InputReader in = new InputReader(System.in); PrintWriter pw = new PrintWriter(System.out); int n = in.readInt(); int k = in.readInt(); int m = in.readInt(); DisjointUnionSets dsu = new DisjointUnionSets(n + 1); for (int i = 0; i < k; i++) { int u = in.readInt(); int v = in.readInt(); dsu.union(u, v); } Edge[] edges = new Edge[m]; // (4 + 4 + 8) * 2M = 32M for (int i = 0; i < m; i++) { int u = in.readInt(); int v = in.readInt(); long p = in.readLong(); edges[i] = new Edge(u, v, p); } Arrays.sort(edges); long res = 1; boolean addedEdge = false; for (Edge edge : edges) { if (!dsu.check(edge.getU(), edge.getV())) { dsu.union(edge.getU(), edge.getV()); res = (res * edge.getP()) % MODULLO; addedEdge = true; } } if (!dsu.isJoint()) { res = -1; } else if (!addedEdge) { res = 0; } pw.println(res); pw.flush(); pw.close(); } public static class DisjointUnionSets { private int[] rank; // 4M private int[] parent; // 4M DisjointUnionSets(int size) { rank = new int[size]; parent = new int[size]; for (int i = 0; i < size; i++) { parent[i] = i; } } int find(int target) { if (parent[target] != target) { parent[target] = find(parent[target]); } return parent[target]; } boolean check(int x, int y) { return find(x) == find(y); } void union(int x, int y) { int xRoot = find(x); int yRoot = find(y); if (xRoot == yRoot) { return; } if (rank[xRoot] < rank[yRoot]) { parent[xRoot] = yRoot; } else if (rank[yRoot] < rank[xRoot]) { parent[yRoot] = xRoot; } else { parent[yRoot] = xRoot; rank[xRoot] = rank[xRoot] + 1; } } boolean isJoint() { int parent = -1; for (int i = 1; i < rank.length; i++) { if (parent == -1) { parent = find(i); } else { if (parent != find(i)) { return false; } } } return true; } } private static class Edge implements Comparable<Edge> { private int u; private int v; private long p; Edge(int u, int v, long p) { this.u = u; this.v = v; this.p = p; } int getU() { return u; } int getV() { return v; } long getP() { return p; } @Override public int compareTo(Edge edge) { return Long.compare(p, edge.p); } } } 

B. Phrases perdues

Publié par: Dmitry Fisko

En examinant les statistiques de l'application, le gestionnaire Vasily a émis l'hypothèse que l'interface manquait de zeste qui retiendrait les utilisateurs. Par conséquent, Vasily a demandé de diversifier l'interface d'application avec le concepteur Maria. Après quelques croquis, Maria a commencé! L'idée du tout début a fait surface - il s'est avéré que vous devez ajouter des animations pour le texte. Nous avons sélectionné plusieurs textes et réalisé des animations. Malheureusement, une fois le travail terminé, les animations ont été mélangées et le texte source de chacune d'entre elles a été perdu. Aidez l'équipe à comprendre quel texte est animé dans chaque cas.

Formats d'E / S, exemple et notes

Format d'entrée


Vous avez plusieurs fichiers texte avec les animations données, pour chacun vous devez sélectionner le texte approprié.

Lien vers l'archive avec des fichiers d'animation .

Chaque animation est définie par des paramètres:
- canvasWidth canvasHeight - la largeur et la hauteur du conteneur pour l'animation, sont spécifiées dans la première ligne d'entrée,
- figuresCount - le nombre de figures à animer est défini dans la deuxième ligne d'entrée,
- rectangle centerX centerY largeur hauteur angle couleur - déclaration d'un rectangle centré en un point (centerX, centerY), largeur × hauteur, degré d'angle de rotation angle et couleur spécifiée,
- cercle centerX centreY rayon couleur - déclaration d'un cercle avec le centre au point (centerX, centerY), le rayon du rayon et la couleur spécifiée.

Le paramètre de couleur peut avoir les valeurs {noir, rouge, blanc, jaune}. Le paramètre d'angle prend des valeurs dans la plage (-359 °, 359 °).

Pour chaque figure, plusieurs types d'animation peuvent être spécifiés à la fois, qui sont appliqués en parallèle. De plus, chaque type d'animation ne peut pas être appliqué plus d'une fois à une figure. Le nombre d'animations appliquées à la figure est défini par le nombre 0 ⩽ figureAnimationsCount ⩽ 3 immédiatement après la déclaration de la figure.

Types d'animations:
- move destX destY time [cycle] - déplace une figure vers un point (destX, destY) en millisecondes.
- rotation de l'angle de temps [cycle] - rotation de la figure par degrés d'angle en millisecondes de temps.
- scale destScale time [cycle] - augmente le chiffre de destScale en millisecondes de temps.

Si le paramètre de cycle est spécifié, alors à la fin de l'animation, son mouvement continue dans la direction opposée.

Format de sortie


Le texte affiché lors de la lecture de l'animation. Pour chaque fichier d'animation, la réponse doit être indiquée sur une nouvelle ligne. La casse des caractères dans la réponse peut être arbitraire. Chaque texte trouvé dans l'animation est évalué à 10 points.

Exemple

EntrerConclusion
400 400
10
rectangle 69.000 280.000 24.000 24.000 0.000 black
1
move 251.000 72.000 10000 cycle
rectangle 256.000 188.000 24.000 48.000 0.000 black
0
rectangle 232.000 152.000 72.000 24.000 0.000 black
0
rectangle 35.000 400.000 24.000 24.000 0.000 black
1
move 285.000 -96.000 10000 cycle
rectangle 244.000 248.000 48.000 24.000 0.000 black
0
rectangle 300.000 117.000 24.000 48.000 0.000 black
1
move 112.000 164.000 5000
rectangle 136.000 200.000 72.000 24.000 0.000 black
0
rectangle 160.000 236.000 24.000 48.000 0.000 black
0
rectangle 208.000 224.000 24.000 72.000 44.797 black
1
rotate -44.797 5000
rectangle 232.000 200.000 24.000 24.000 0.000 black
0
42

Remarques


Visualisation de l'animation Ă  partir de l'exemple:


Solution


La tâche consistait à mettre en œuvre l'animation. Si l'animation est correctement implémentée, lors de la lecture, de nombreuses primitives sont ajoutées dans du texte lisible. L'animation peut être réalisée par n'importe quel moyen - intégré dans des plates-formes mobiles ou des solutions de niveau inférieur.

Exemple d'animation ( lien vers le fichier .mov ):



Un exemple d'implémentation utilisant la bibliothèque Tkinter de Python
 import copy import math import time import tkinter from enum import Enum FRAMES_PER_SECOND = 60 TIME_BETWEEN_FRAMES = int(1000 / FRAMES_PER_SECOND) class Rectangle(object): def __init__(self, left_x, left_y, width, height, color, angle=0.0): self.center_x = left_x + width / 2 self.center_y = left_y + height / 2 self.width = width self.height = height self.color = color self.angle = angle def get_left_x(self): return self.center_x - self.width / 2 def set_left_x(self, left_x): self.center_x = left_x + self.width / 2 def get_left_y(self): return self.center_y - self.height / 2 def set_left_y(self, left_y): self.center_y = left_y + self.height / 2 left_x = property(get_left_x, set_left_x) left_y = property(get_left_y, set_left_y) class Circle(object): def __init__(self, left_x, left_y, radius, color): self.center_x = left_x + radius self.center_y = left_y + radius self.radius = radius self.color = color self.angle = 0 def get_left_x(self): return self.center_x - self.radius def set_left_x(self, left_x): self.center_x = left_x + self.radius def get_left_y(self): return self.center_y - self.radius def set_left_y(self, left_y): self.center_y = left_y + self.radius left_x = property(get_left_x, set_left_x) left_y = property(get_left_y, set_left_y) class AnimationType(Enum): MOVE = 1 ROTATE = 2 SCALE = 3 class Animation(object): def __init__(self, time, cycle): self.time = time self.cycle = cycle def scale(self, time_spent): return 1 def move(self, time_spent): return 0, 0 def angle(self, time_spent): return 0 def _cycle_progress(self, time_spent): if self.cycle: cycle_time = time_spent % (self.time * 2) if cycle_time > self.time: return 1 - (cycle_time - self.time) / self.time else: return cycle_time / self.time else: if time_spent < self.time: cycle_time = time_spent % self.time return cycle_time / self.time else: return 1 class MoveAnimation(Animation): def __init__(self, figure, to_x, to_y, time, cycle): super().__init__(time, cycle) self.from_x = figure.center_x self.from_y = figure.center_y self.to_x = to_x self.to_y = to_y def move(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) diff_x = self.to_x - self.from_x diff_y = self.to_y - self.from_y return diff_x * cycle_progress, diff_y * cycle_progress class ScaleAnimation(Animation): def __init__(self, destination_scale, time, cycle): super().__init__(time, cycle) self.destination_scale = destination_scale def scale(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) return 1 + (self.destination_scale - 1) * cycle_progress class RotateAnimation(Animation): def __init__(self, rotate_angle, time, cycle): super().__init__(time, cycle) self.rotate_angle = rotate_angle def angle(self, time_spent): cycle_progress = super()._cycle_progress(time_spent) return self.rotate_angle * cycle_progress class Transformer(object): def scale(self, scale): pass def move(self, diff_x, diff_y): pass def rotate(self, angle): pass class RectangleTransformer(Transformer): def __init__(self, rectangle): self.rectangle = rectangle def scale(self, scale): self.rectangle.width = self.rectangle.width * scale self.rectangle.height = self.rectangle.height * scale def move(self, diff_x, diff_y): self.rectangle.center_x = self.rectangle.center_x + diff_x self.rectangle.center_y = self.rectangle.center_y + diff_y def rotate(self, angle): self.rectangle.angle = (self.rectangle.angle + angle) % 360 class CircleTransformer(Transformer): def __init__(self, circle): self.circle = circle def scale(self, scale): self.circle.radius = self.circle.radius * scale def move(self, diff_x, diff_y): self.circle.center_x = self.circle.center_x + diff_x self.circle.center_y = self.circle.center_y + diff_y def rotate(self, angle): pass class Drawer(object): def draw(self, canvas, spent_time): pass class AnimationApplier(object): @staticmethod def apply_animations(spent_time, transformer, animations): for animation in animations: transformer.scale(animation.scale(spent_time)) diff_x, diff_y = animation.move(spent_time) transformer.move(diff_x, diff_y) transformer.rotate(animation.angle(spent_time)) class RectangleDrawer(Drawer): def __init__(self, rectangle, animations): self.rectangle = rectangle self.animations = animations def draw(self, canvas, spent_time): rect = self._transform_rect_with_animations(spent_time) rect_points = self._get_rectangle_points(rect) rotated_points = self._rotate(rect_points, rect.angle, [rect.center_x, rect.center_y]) canvas.create_polygon(rotated_points, fill=rect.color) def _transform_rect_with_animations(self, spent_time): rect = copy.copy(self.rectangle) transformer = RectangleTransformer(rect) AnimationApplier.apply_animations(spent_time, transformer, self.animations) return transformer.rectangle @staticmethod def _get_rectangle_points(rect): half_width = rect.width / 2 half_height = rect.height / 2 return [ [rect.center_x - half_width, rect.center_y - half_height], [rect.center_x - half_width, rect.center_y + half_height], [rect.center_x + half_width, rect.center_y + half_height], [rect.center_x + half_width, rect.center_y - half_height] ] @staticmethod def _rotate(points, angle, center): angle = math.radians(angle) cos_val = math.cos(angle) sin_val = math.sin(angle) cx, cy = center new_points = [] for x_old, y_old in points: x_old -= cx y_old -= cy x_new = x_old * cos_val - y_old * sin_val y_new = x_old * sin_val + y_old * cos_val new_points.append([x_new + cx, y_new + cy]) return new_points class CircleDrawer(Drawer): def __init__(self, circle, animations): self.circle = circle self.animations = animations def draw(self, canvas, spent_time): circle = self._transform_rect_with_animations(spent_time) size = circle.radius * 2 canvas.create_oval(circle.left_x, circle.left_y, circle.left_x + size, circle.left_y + size, fill=circle.color) def _transform_rect_with_animations(self, spent_time): circle = copy.copy(self.circle) transformer = CircleTransformer(circle) AnimationApplier.apply_animations(spent_time, transformer, self.animations) return transformer.circle class Scene(object): def __init__(self, canvas, animated_symbols): self.canvas = canvas self.drawers = self._get_generated_drawers(animated_symbols) @staticmethod def _get_generated_drawers(animated_symbols): drawers = [] for animated_symbol in animated_symbols: figure = animated_symbol.figure animations = animated_symbol.animations if isinstance(figure, Rectangle): drawer = RectangleDrawer(figure, animations) elif isinstance(figure, Circle): drawer = CircleDrawer(figure, animations) else: raise Exception() drawers.append(drawer) return drawers def draw(self, time_spent): self.canvas.delete("all") for drawer in self.drawers: drawer.draw(self.canvas, time_spent) self.canvas.pack() class Timer(object): def __init__(self): self.started_time = None def start(self): self.started_time = self._current_time() def time_spent(self): current_time = self._current_time() return current_time - self.started_time @staticmethod def _current_time(): return int(round(time.time() * 1000)) def scene_loop(window, scene, timer): time_spent = timer.time_spent() scene.draw(time_spent) window.after(TIME_BETWEEN_FRAMES, scene_loop, window, scene, timer) def configure_window_location(window, canvas_width, canvas_height): screen_width = window.winfo_screenwidth() screen_height = window.winfo_screenheight() x = (screen_width / 2) - (canvas_width / 2) y = (screen_height / 2) - (canvas_height / 2) window.geometry('%dx%d+%d+%d' % (canvas_width, canvas_height, x, y)) class AnimatedSymbol(object): def __init__(self, figure, animations): self.figure = figure self.animations = animations class AnimationsParser(object): def parse_animated_symbols(self, file_path): with open(file_path) as f: lines = f.readlines() canvas_width, canvas_height = self._parse_size(lines[0]) symbols_count = int(lines[1]) animated_symbols = [] current = 2 for i in range(symbols_count): figure = self._parse_figure(lines[current]) current += 1 symbol_animations = self._parse_animations(figure, current, lines) current += 1 + len(symbol_animations) animated_symbol = AnimatedSymbol(figure, symbol_animations) animated_symbols.append(animated_symbol) return canvas_width, canvas_height, animated_symbols def _parse_size(self, line): parts = line.split(" ") return int(parts[0]), int(parts[1]) def _parse_animations(self, figure, current, lines): animations = [] animations_count = int(lines[current]) for i in range(animations_count): parts = lines[current + i + 1].split(" ") animation = self._parse_animation(figure, parts) animations.append(animation) return animations def _parse_figure(self, line): parts = line.split(" ") if parts[0] == 'rectangle': center_x = float(parts[1]) center_y = float(parts[2]) width = float(parts[3]) height = float(parts[4]) angle = float(parts[5]) color = parts[6].replace('\n', '') figure = Rectangle(center_x - width / 2, center_y - height / 2, width, height, color, angle) elif parts[0] == 'circle': center_x = float(parts[1]) center_y = float(parts[2]) radius = float(parts[3]) color = parts[4].replace('\n', '') figure = Circle(center_x - radius, center_y - radius, radius, color) else: raise Exception() return figure def _parse_animation(self, figure, parts): if parts[0] == "move": animation = self._parse_move_animation(figure, parts) elif parts[0] == "scale": animation = self._parse_scale_animation(parts) elif parts[0] == "rotate": animation = self._parse_rotate_animation(parts) else: raise Exception() return animation @staticmethod def _parse_move_animation(figure, parts): to_x = float(parts[1]) to_y = float(parts[2]) time = float(parts[3]) cycle = len(parts) >= 5 return MoveAnimation(figure, to_x, to_y, time, cycle) @staticmethod def _parse_scale_animation(parts): destination_scale = float(parts[1]) time = float(parts[2]) cycle = len(parts) >= 4 return ScaleAnimation(destination_scale, time, cycle) @staticmethod def _parse_rotate_animation(parts): rotate_angle = float(parts[1]) time = float(parts[2]) cycle = len(parts) >= 4 return RotateAnimation(rotate_angle, time, cycle) def main(): canvas_width, canvas_height, animated_symbols = \ AnimationsParser().parse_animated_symbols('animation_path.txt') window = tkinter.Tk() configure_window_location(window, canvas_width, canvas_height) canvas = tkinter.Canvas(window, width=canvas_width, height=canvas_height) scene = Scene(canvas, animated_symbols) timer = Timer() timer.start() window.after(TIME_BETWEEN_FRAMES, scene_loop, window, scene, timer) window.mainloop() if __name__ == "__main__": main() 

C. Tetris bot

Auteur: Dmitry Nevedomsky
Limite de temps1 s
Limite de mémoire256 Mo
Entrerentrée standard ou input.txt
Conclusionsortie standard ou output.txt
Occupé Arkady aime passer son temps libre à participer à diverses ventes aux enchères de rétrocomputer. Sur l'un d'eux, il a vu le Commodoro qui avait vu la vie, qu'Arkady s'est empressé d'acheter. Et cette fois, il a acquis non seulement du matériel, mais aussi des logiciels, une surprise l'attendait sur l'ordinateur - il y avait Tetris inachevé par le propriétaire précédent. Empiriquement, Arkady a démêlé ses règles.

Le joueur se voit offrir un champ Tetris de 10 cellules de large et de hauteur illimitée. Une liste de tous les tetramino qui doivent être placés sur le terrain. Le tétramino «tombe» immédiatement, c'est-à-dire que sa position est réglée une fois pour le mouvement correspondant, et pendant la chute, la position et la rotation ne peuvent pas être modifiées.

Tetramino tombe jusqu'à ce qu'il trébuche sur le fond ou sur une autre figure. Si en même temps une ligne entière du champ est remplie, cette ligne disparaît et tout ce qui est au-dessus tombe une cellule en dessous.

Le nombre de points obtenus est déterminé par le nombre de lignes détruites. Le jeu se termine lorsque toutes les pièces proposées ont été placées. L'auteur du jeu a laissé derrière lui une table des records, dans laquelle il a lui-même pris toutes les places.

Comme mentionné précédemment, Arkady est une personne très occupée. Mais il joue aussi beaucoup. Aidez-le à devenir le meilleur dans ce jeu.

Tableau des figures et leurs tournants possibles

Formats d'E / S, exemple et notes

Format d'entrée


Les données d'entrée se trouvent dans les fichiers input1.txt, input2.txt, ..., input10.txt. Archiver avec des fichiers .

La première ligne contient un nombre positif de chiffres N (N ≤ 100).

Dans chaque ligne suivante, un seul caractère latin de l'ensemble {'O', 'S', 'Z', 'L', 'J', 'T', 'I'} est transmis.

Chaque symbole représente une figure tétramino.

Format de sortie


Pour vérification, vous devez soumettre une archive zip avec les fichiers de sortie situés à sa racine avec les noms output1.txt, output2.txt, ..., output10.txt, où le fichier de sortie outputX.txt doit correspondre au fichier d'entrée inputX.txt.
Le fichier de réponses se compose de N lignes, chacune correspondant à un tétramino des données d'entrée et se compose de deux nombres A i et B i , indiquant le mouvement du joueur avec la figure proposée:

1. A i indique la colonne de l'élément tétramino le plus à gauche (1 ≤ A i ≤ 10).
2. B i indique le sens de rotation du tétramino (1 ≤ B i ≤ 4).

Si le fichier requis n'est pas dans l'archive ou a été mal formé, alors 0 point sera attribué pour le test correspondant.

Exemple

EntrerConclusion
15
Z
O
L
O
J
S
T
J
Z
J
Z
S
Z
I
O
1 2
9 1
6 4
9 1
3 2
6 1
1 2
4 1
6 2
3 1
8 2
2 2
4 1
10 1
6 1

Remarques


Dans cet exemple, 4 lignes seront détruites.

L'Ă©tat final du champ:



Informations sur les codes d'erreur:

1. PE - une erreur de sortie a été commise (par exemple, il n'y a pas une telle rotation de la figure)
2. WA - des informations sur la rotation et la position de la figure ont été fournies correctement, mais cette disposition dépasse les limites du champ.
3. TL, ML, OK, RE - ces codes ont la même signification que dans les règles du système Yandex.Contest.

Le problème étant NP-complet, la solution ne doit pas être optimale d'un point de vue mathématique. L'efficacité de la décision est déterminée par le nombre de points marqués.

Solution


Il s'agit d'une tâche de test ouverte. Voyons ce que cela signifie.

Le participant reçoit à sa disposition une archive zip, à la racine de laquelle se trouvent les fichiers texte input1.txt, input2.txt, ..., input10.txt. Un fichier est un ensemble de figures tétraminos à organiser. En réponse du participant, une archive zip générée est attendue, à la racine de laquelle se trouvent les fichiers output1.txt, output2.txt, ..., output10.txt. Chaque fichier doit contenir des déplacements par morceaux dans le même ordre dans lequel ils sont spécifiés dans le fichier de l'ensemble correspondant.

La tâche est NP-complete et la solution à cent pour cent n'est qu'une recherche exhaustive. Par conséquent, la complexité croît de façon exponentielle avec l'augmentation du nombre de chiffres. Puisqu'il n'est pas nécessaire de remettre le code de la solution, il n'y a aucune restriction sur celui-ci en mémoire et en temps d'exécution.

Un point complet a été attribué à la tâche lorsqu'une qualité «satisfaisante» a été obtenue - la solution aurait dû obtenir 60% des points. La solution de l’auteur au problème a tout autant marqué, qui était basée sur un algorithme génétique formé sur un randomiseur Tetris standard. Ce randomiseur est décrit dans l'article .

Le reste de la tâche n'est pas très différent de l'écriture d'un bot régulier pour tetris, sauf que le "verre" du terrain de jeu est illimité en hauteur. Par conséquent, le succès de la décision n'est pas déterminé par la durée pendant laquelle le bot peut jouer sans aller au-delà du «verre», mais par son succès à organiser un ensemble limité de chiffres.

Le participant était libre de choisir une stratégie de solution. Voici certains d'entre eux par ordre croissant de complexité et par augmentation du nombre de points marqués:

1. Nous jouons des «mains» sans utiliser le code, en notant les mouvements dans la solution.
2. Nous Ă©crivons un visualiseur et jouons des "mains", notant les mouvements dans la solution.
3. L'algorithme gourmand. A chaque tour, nous essayons de mettre la pièce de sorte que le nombre de lignes détruites soit maximum. S'il existe plusieurs de ces options, sélectionnez-en une au hasard.
4. Le même algorithme gourmand que dans le paragraphe 3, seulement il est vérifié non pas le succès de la définition d'un chiffre, mais le succès de la recherche exhaustive d'un groupe de chiffres (par exemple, un groupe de cinq chiffres).
5. L'algorithme génétique. On prend en compte dans la fonction fitness non seulement le nombre de lignes cassées, comme dans l'algorithme gourmand, mais aussi la finesse du «relief» du champ et, par exemple, le nombre d'espaces libres formés entre les figures.

D et E. Quest

Auteurs: Ilimdar Ablyaev et Ilya Kuteev

Misha fait un jeu mobile dans le genre de quête. Il a trouvé sur Internet une bibliothèque prête à l'emploi avec un moteur pour le jeu et en a fait une interface. Misha est pressé de sortir le jeu et a fait plusieurs erreurs au cours du processus de développement. Pour terminer le projet à temps, Misha vous a demandé de l'aide. Téléchargez le projet, corrigez les erreurs de compilation, les plantages et les erreurs logiques. «», . .

: Android , iOS . — ( 255 ).

Android


1) .

2) , IDE.

  1. final mBackButton mTitleTextView, .
  2. mTitleTextView mBackButton , , .
  3. — Layout ViewGroup.
  4. okButton getTextTaskControlsLayout.
  5. IDE , «» «» .

3) . ds , «java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity». .

4) .

  • — java.lang.IndexOutOfBoundsException.
  • : Layout Layout. .
  • 1.1 ( ).

5) .

  • , .
  • ( , , ).
  • . :
    — Layout.
    — .
  • .

6) . — .

7) «».

  • android.content.res.Resources$NotFoundException.
  • . , ID .
  • .

! .

iOS


. :



, Optional<Optional<Step>>. guard let Optional . Optional, flatMap({ $0 }), .



, .



, QuestCore . :



, — goLeft, goRight . , goRight .



, : TestView, firstResponder . , TestView firstResponder. , UIKit.



«». IBOutlet, , . .







, 2018 .

Source: https://habr.com/ru/post/fr482210/


All Articles