Хорошее решение для этого алгоритмического кода головоломки (USACO)?

Я обнаружил решение, посмотрев исходный код Jenkins:

parameters{ text(name: 'mytextparam', 
                 defaultValue: 'Default lines for the parameter', 
                 description: 'A description of this param')    
}

Появляется многострочное текстовое приглашение ввода, которое становится значением параметра, которое вы можете ссылаться позже как params.mytextparam

Это не документировано в документации Jenkins Pipeline, поэтому могут возникнуть такие проблемы, как отсутствие поддержки или снятие в будущей версии. Или это может пойти другим путем, и они могут документировать его в следующем выпуске.

4
задан Hp_issei 19 January 2019 в 15:13
поделиться

3 ответа

Один из способов решить эту проблему с помощью BFS. Вы можете ссылаться на код ниже для деталей. Надеюсь, это поможет.

import java.util.*;
import java.io.*;

public class SnowBoots {

    public static int n;
    public static int[] deep;
    public static int nBoots;
    public static Boot[] boots;

    public static void main(String[] args) throws Exception {

        // Read the grid.
        Scanner stdin = new Scanner(new File("snowboots.in"));

        // Read in all of the input.
        n = stdin.nextInt();
        nBoots = stdin.nextInt();
        deep = new int[n];
        for (int i = 0; i < n; ++i) {
            deep[i] = stdin.nextInt();
        }
        boots = new Boot[nBoots];
        for (int i = 0; i < nBoots; ++i) {
            int d = stdin.nextInt();
            int s = stdin.nextInt();
            boots[i] = new boot(d, s);
        }

        PrintWriter out = new PrintWriter(new FileWriter("snowboots.out"));
        out.println(bfs());
        out.close();
        stdin.close();
    }

    // Breadth First Search Algorithm [https://en.wikipedia.org/wiki/Breadth-first_search]
    public static int bfs() {

        // These are all valid states.
        boolean[][] used = new boolean[n][nBoots];
        Arrays.fill(used[0], true);

        // Put each of these states into the queue.
        LinkedList<Integer> q = new LinkedList<Integer>();
        for (int i = 0; i < nBoots; ++i) {
            q.offer(i);
        }

        // Usual bfs.
        while (q.size() > 0) {

            int cur = q.poll();
            int step = cur / nBoots;
            int bNum = cur % nBoots;

            // Try stepping with this boot...
            for (int i = 1; ((step + i) < n) && (i <= boots[bNum].maxStep); ++i) {
                if ((deep[step+i] <= boots[bNum].depth) && !used[step+i][bNum]) {
                    q.offer(nBoots * (step + i) + bNum);
                    used[step + i][bNum] = true;
                }
            }

            // Try switching to another boot.
            for (int i = bNum + 1; i < nBoots; ++i) {
                if ((boots[i].depth >= deep[step]) && !used[step][i]) {
                    q.offer(nBoots * step + i);
                    used[step][i] = true;
                }
            }
        }

        // Find the earliest boot that got us here.
        for (int i = 0; i < nBoots; ++i) {
            if (used[n - 1][i]) {
                return i;
            }
        }

        // Should never get here.
        return -1;
    }
}

class Boot {

    public int depth;
    public int maxStep;

    public Boot(int depth, int maxStep) {
        this.depth = depth;
        this.maxStep = maxStep;
    }
}
0
ответ дан Yaman Jain 19 January 2019 в 15:13
поделиться

Вы можете решить эту проблему с помощью динамического программирования. Вы можете увидеть концепцию в этой ссылке (просто прочитайте раздел «Компьютерное программирование»). Он имеет следующие два шага.

  • Сначала решите проблему рекурсивно.
  • Помните состояния.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mx 100005
#define mod 1000000007
int n, b;
int f[333], s[333], d[333];
int dp[251][251];
int rec(int snowPos, int bootPos)
{
    if(snowPos == n-1){
        return 0;

    int &ret = dp[snowPos][bootPos];
    if(ret != -1) return ret;
    ret = 1000000007;
    for(int i = bootPos+1; i<b; i++)
    {
        if(s[i] >= f[snowPos]){
            ret = min(ret, i - bootPos + rec(snowPos, i));
        }
    }
    for(int i = 1; i<=d[bootPos] && snowPos+i < n; i++){
        if(f[snowPos + i] <= s[bootPos]){
            ret = min(ret, rec(snowPos+i, bootPos));
        }
    }
    return ret;
}
int main()
{
    freopen("snowboots.in", "r", stdin);
    freopen("snowboots.out", "w", stdout);
    scanf("%d %d", &n, &b);
    for(int i = 0; i<n; i++)
        scanf("%d", &f[i]);
    for(int i = 0; i<b; i++){
        scanf("%d %d", &s[i], &d[i]);
    }
    memset(dp, -1, sizeof dp);
    printf("%d\n", rec(0, 0));

    return 0;
}

Это мое решение этой проблемы (на C ++). Это просто рекурсия. Как говорит проблема,

  • вы можете изменить загрузку, или
  • вы можете сделать прыжок при текущей загрузке.

Запоминание выполняется двумерным массивом dp [] [].

0
ответ дан Ovishek Paul 19 January 2019 в 15:13
поделиться

Да, вы можете спасти ваше решение, добавив дополнительный for -кольцо.

Что вам нужно сделать, так это то, что если вы обнаружите, что ваша предыдущая пара ботинок может привести вас к плитке, которая слишком глубоко в снегу для вашей следующей пары, то вам нужно попытаться «вернуться» к последней плитке. это не слишком глубоко. В итоге получается решение в худшем случае O ( N · B ) времени и O (1) дополнительного пространства. [ 1161]

Может быть не очевидно, почему можно возвращаться к этой плитке - в конце концов, только потому, что вы можете достичь данной плитки, это не обязательно означает, что вы смогли добраться до всех плиток до нее - поэтому позвольте мне объясните немного, почему это нормально хорошо.

Пусть maxReachableTileNum будет номером (от 1 до N ) последней плитки, которой вы смогли достичь с вашими предыдущими ботинками, и пусть lastTileNumThatsNotTooDeep будет числом (от 1 до [ 1130] N ) последней плитки на или перед maxReachableTileNum, которая не слишком покрыта снегом для вашей следующей пары. (Мы знаем, что есть такая плитка, потому что плитка # 1 вообще не имеет снега, поэтому, если ничего другого, мы не знаем, что мы можем вернуться к самому началу.) Теперь, так как мы смогли получить до maxReachableTileNum, затем некоторая предыдущая загрузка должна была либо наступить на lastTileNumThatsNotTooDeep (в этом случае нет проблем, она достижима), либо пропустить его до некоторой более поздней плитки (до или после maxReachableTileNum) , Но эта более поздняя плитка должна быть глубже, чем lastTileNumThatsNotTooDeep (потому что эта более поздняя плитка больше, чем с currentBootNum , что, по крайней мере, не меньше глубины lastTileNumThatsNotTooDeep ), что означает, что ботинок, который пропустил lastTileNumThatsNotTooDeep, безусловно, мог бы наступить на lastTileNumThatsNotTooDeep вместо этого: это означало бы сделать более короткий шаг (ОК) на плитку с менее глубоким покрытием (ОК ) чем то, что он на самом деле сделал. Так или иначе, мы знаем, что lastTileNumThatsNotTooDeep было достижимо. Поэтому мы можем попытаться вернуться к lastTileNumThatsNotTooDeep. (Примечание: приведенный ниже код использует имя reachableTileNum вместо lastTileNumThatsNotTooDeep, потому что он продолжает использовать переменную reachableTileNum для поиска вперед, чтобы найти достижимые тайлы.)

Однако нам все еще нужно удерживать на предыдущий maxReachableTileNum: возврат может оказаться бесполезным (потому что он может не позволить нам продвинуться вперед дальше, чем у нас уже есть), в этом случае мы просто откажемся от этих ботинок и перейдем к следующему пара, с maxReachableTileNum в его предыдущем значении.

Итак, в целом, мы имеем это:

public static int solve(
    final int[] tileSnowDepths,           // tileSnowDepths[0] is f_1
    final int[] bootAllowedDepths,        // bootAllowedDepths[0] is s_1
    final int[] bootAllowedTilesPerStep   // bootAllowedTilesPerStep[0] is d_1
) {
    final int numTiles = tileSnowDepths.length;
    final int numBoots = bootAllowedDepths.length;
    assert numBoots == bootAllowedTilesPerStep.length;

    int maxReachableTileNum = 1; // can reach tile #1 even without boots

    for (int bootNum = 1; bootNum <= numBoots; ++bootNum) {
        final int allowedDepth = bootAllowedDepths[bootNum-1];
        final int allowedTilesPerStep = bootAllowedTilesPerStep[bootNum-1];

        // Find the starting-point for this boot -- ideally the last tile
        // reachable so far, but may need to "backtrack" if that tile is too
        // deep; see explanation above of why it's safe to assume that we
        // can backtrack to the latest not-too-deep tile:

        int reachableTileNum = maxReachableTileNum;
        while (tileSnowDepths[reachableTileNum-1] > allowedDepth) {
            --reachableTileNum;
        }

        // Now see how far we can go, updating both maxReachableTileNum and
        // reachableTileNum when we successfully reach new tiles:

        for (int tileNumToTry = maxReachableTileNum + 1;
             tileNumToTry <= numTiles
                 && tileNumToTry <= reachableTileNum + allowedTilesPerStep;
             ++tileNumToTry
        ) {
            if (tileSnowDepths[tileNumToTry-1] <= allowedDepth) {
                maxReachableTileNum = reachableTileNum = tileNumToTry;
            }
        }

        // If we've made it to the last tile, then yay, we're done:

        if (maxReachableTileNum == numTiles) {
            return bootNum - 1; // had to discard this many boots to get here
        }
    }

    throw new IllegalArgumentException("Couldn't reach last tile with any boot");
}

(я проверил это на примере данных USACO, и он вернул 2, как и ожидалось.)

Это может быть оптимизированы далее, например с логикой, чтобы пропустить пары загрузок, которые явно не полезны (потому что они не более сильные и не более гибкие, чем предыдущая успешная пара), или с дополнительной структурой данных, чтобы отслеживать позиции последних минимумов (чтобы оптимизировать возврат) процесс), или с логикой, чтобы избежать возврата назад дальше, чем это возможно; но учитывая, что N · B & nbsp; ≤ & nbsp; 250 2 = & nbsp; = & nbsp; 62,500, я не думаю, что такая оптимизация оправдана. [1 167]


Отредактировано, чтобы добавить (2019-02-23): я подумал об этом дальше, и мне пришло в голову, что на самом деле можно написать решение в худшем случае [1137 ] O ( N + nbsp; B log N ) времени (которое асимптотически лучше, чем O ] ( N · B )) и O ( N ) дополнительное пространство. Но это намного сложнее; он включает в себя три дополнительные структуры данных (одну для отслеживания позиций последних минимумов, чтобы обеспечить возможность обратного отслеживания за O (log & nbsp; N ) время; одну для отслеживания позиций из будущих минимумов, чтобы разрешить проверку за O (log & nbsp; N ), если откат на самом деле полезен (и если да, то двигаться вперед к соответствующему минимуму ) и один для поддержания необходимой прогнозной информации, чтобы второй был сохранен в амортизированном O (1) времени). Также сложно объяснить, почему это решение гарантированно находится в пределах O ( N + B & nbsp; log & nbsp; N ]) время (потому что оно включает в себя много амортизированного анализа , и внесение незначительного изменения, которое может показаться оптимизацией - например, замена линейного поиска на бинарный поиск - может нарушить анализ и фактически ] увеличивают сложность времени в наихудшем случае. Поскольку N и B оба, как известно, составляют не более 250, я не думаю, что все осложнения того стоят. [ 1168]

0
ответ дан ruakh 19 January 2019 в 15:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: