Why are the restaurant cars in Hungarian trains closed in Germany?

While on a Hungarian MÁV-START train, I noticed an odd sign saying that due to regulation, the bistro carriage will not be serving food while in Germany. From some googling, it seems that German trains often do have bistro carriages too, so I’m wondering whether this is some temporary rule due to the pandemic or something else?

Why is it forbidden to eat in Hungarian trains while in Germany?

While on a Hungarian MÁV-START train, I noticed an odd sign saying that due to regulation, the bistro carriage will not be serving food while in Germany. From some googling, it seems that German trains often do have bistro carriages too, so I’m wondering whether this is some temporary rule due to the pandemic or something else?

java – Hungarian algorithm for optimal assignment

The assignment problem is about assigning tasks to workers, where each pair (worker, task) has a cost. The Hungarian algorithm builds an optimal solution for this problem. It has two variants, depending on the representation of the cost relationships: one with bipartite graphs, and one with cost matrices. I have implemented the latter form as described here because I had trouble making Wikipedia’s step 3 example into an algorithm. The goal is to produce a library that I can use in another project in which I intend to assign mentors to students based on their likelihood of getting along.

In short, the algorithm modifies the cost matrix to make zeroes appear and selects some of them to build an optimal solution. It uses two types of marks, stars (for zeroes belonging to the candidate solution) and primes (for zeroes that could replace starred zeroes in the solution). It moves between 6 steps:

  1. The matrix is preprocessed (validity checks, optional rotation to have at least as many columns than rows, subtraction of the minimum value of each row to all values of the same row and then the same for columns)
  2. Greedily prepare a candidate solution by starring zeroes
  3. Check if the candidate solution is complete; if yes, return it; else, go to the next step
  4. Try to find a zero that could replace a starred one in the candidate solution; if one is found, go to the next step; else, go to step 6
  5. Follow an alternating path of primed and starred zeroes to modify the candidate solution, and go back to step 3
  6. Modify some values in the matrix to make more zeroes appear, and go back to step 4.

For example, take the following cost matrix.

0 1 2 3
0 1 2 25 13
1 5 7 25 15
2 10 13 16 13
3 17 21 11 18
4 15 15 15 14

The algorithm must output the following solution, with a total cost of 2 + 5 + 13 + 11 = 31 (and the last row is not assigned any task because the worker is too expensive).

0 1 2 3
0 X
1 X
2 X
3 X
4

My code in Java 11 works as intended on several hand-made examples and standard edge cases (null input, non-square cost matrix, worst-case matrix for this algorithm). If you find any case that is poorly handled, I would be happy to know but that is not my main concern here.

I used junit5 to write unit tests and I feel that the test framework I wrote might be needlessly complex. I wanted to get a separate test result for each run of a test method on each of its input, and several test methods will have the same input.
I wrote the TestFrameWork and TestArguments interfaces as a way to use junit5’s streams of tests, and each test class must implement the TestFrameWork interface with a utility class implementing the TestArguments interface. Then, most tests only need writing a simple lambda function performing only the actual verification. I wonder if that is a good idea or a terrible one.

Additionally, I made three methods package-private rather than private because I wanted to test them specifically, which smells fishy as well: the constructor, reduceInitialMatrix and getState (which only exist to test reduceInitialMatrix, and that seems even worse). I have a bit of trouble setting the balance between encapsulation and testing, I’ll gladly take any advice on this point too.

All the original code base can be found on my github. For this question, I have edited it a bit: instead of making HungarianSolver extend the abstract Solver and inherit the javadoc, I made the HungarianSolver stand-alone and put the javadoc directly in it. I built and ran the tests on the modified version to make sure that I didn’t break anything.

HungarianSolver.java

package AssignmentProblem;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Implementation of the HungarianAlgorithm.
 */
class HungarianSolver {

    final private int()() costMatrix;
    final private int()() assignments;
    final private int() rowAssignments;
    final private int() colAssignments;
    final private int nRows;
    final private int nCols;
    final private boolean() coveredRows;
    final private boolean() coveredCols;
    final private int() starredRows;
    final private int() starredCols;
    final private int() primedRows;
    final private int() primedCols;
    private int numberCoveredCols;
    final private boolean transposed;

    /**
     * Instantiates a new solver on the given cost matrix. The proper way to get
     * the solution of the assignment problem with a {@link HungarianSolver} is
     * to call {@link #initialise(int()())} rather than directly the constructor.
     *
     * @param costMatrix
     */
    HungarianSolver(int()() costMatrix) {
        Solver.checkMatrixValidity(costMatrix);
        if (costMatrix.length > costMatrix(0).length){
            //flip matrix to have more columns than rows
            transposed = true;
            nRows = costMatrix(0).length;
            nCols = costMatrix.length;
            this.costMatrix = new int(nRows)(nCols);
            for (int i = 0; i < nRows; i++) {
                for (int j = 0; j < nCols; j++) {
                    this.costMatrix(i)(j) = costMatrix(j)(i);
                }
            }
        } else {
            this.costMatrix = costMatrix;
            nRows = costMatrix.length;
            nCols = costMatrix(0).length;
            transposed = false;
        }
        assignments = new int(nRows)(2);
        rowAssignments = new int(transposed ? nCols : nRows);
        colAssignments = new int(transposed ? nRows : nCols);
        coveredRows = new boolean(nRows);
        coveredCols = new boolean(nCols);
        starredRows = new int(nRows);
        starredCols = new int(nCols);
        primedRows = new int(nRows);
        primedCols = new int(nCols);
        Arrays.fill(starredRows, -1);
        Arrays.fill(starredCols, -1);
        Arrays.fill(primedRows, -1);
        Arrays.fill(primedCols, -1);
        Arrays.fill(rowAssignments, -1);
        Arrays.fill(colAssignments, -1);
        for (int() assignment : assignments) {
            Arrays.fill(assignment, -1);
        }
    }

    protected static HungarianSolver initialise(int()() costMatrix) {
        HungarianSolver result = new HungarianSolver(costMatrix);
        result.reduceInitialMatrix();
        result.solveReducedMatrix();
        return result;
    }

    /**
     * Returns the column index assigned to each row.
     * @return The result of the assignment problem from the row perspective.
     * The i-th element of the output is the index of the column assigned to the
     * i-th row, or -1 if the row has not been assigned.
     */
    public int() getRowAssignments() {
        return this.rowAssignments;
    }

    public int() getColumnAssignemnts() {
        return this.colAssignments;
    }

    /**
     * Returns the pairs of row and column indices of the assignments.
     * @return The result of the assignment problem as pairs. Each element of 
     * the output is an assigned pair whose first element is the index of the 
     * row and the second element is the index of the column. Unassigned rows
     * and columns are not included.
     */
    public int()() getAssignments() {
        return this.assignments;
    }

    /**
     * Reduces the values of the matrix to make zeroes appear. This
     * corresponds to the first step of the Hungarian Algorithm.
     */
    void reduceInitialMatrix() {
        //first part: reduce all rows
        for (int() row : costMatrix) {
            int min = row(0);
            for (int val : row) {
                if (val < min) {
                    min = val;
                }
            }
            for (int j = 0; j < row.length; j++) {
                row(j) -= min;
            }
        }
        //second part: reduce all columns
        for (int j = 0; j < nCols; j++) {
            int min = costMatrix(0)(j);
            for (int() row : costMatrix) {
                if (row(j) < min) {
                    min = row(j);
                }
            }
            for (int() row : costMatrix) {
                row(j) -= min;
            }
        }
    }

    /**
     * Performs the main loop of the Hungarian algorithm.
     */
    private void solveReducedMatrix() {
        //Steps 0 and 1 have been preprocessed
        //Step 2 : initial zero starring
        for (int i = 0; i < nRows; i++) {
            for (int j = 0; j < nCols; j++) {
                if (costMatrix(i)(j) == 0 && starredCols(j) == -1) {
                    coveredCols(j) = true;
                    numberCoveredCols++;
                    starredRows(i) = j;
                    starredCols(j) = i;
                    break;
                }
            }
        }
        while (numberCoveredCols < nRows) {
            int() position = primeZero();
            while (position == null){
                //Perform step 6
                //Get minimal unmarked value
                int min = Integer.MAX_VALUE;
                for (int i = 0; i < nRows; i++) {
                    if (coveredRows(i)) {
                        continue;
                    }
                    for (int j = 0; j < nCols; j++) {
                        if (coveredCols(j)) {
                            continue;
                        }
                        if (costMatrix(i)(j) < min) {
                            min = costMatrix(i)(j);
                            if (min == 1){
                                break;
                            }
                        }
                        if (min == 1){
                            break;
                        }
                    }
                }
                //modify the matrix
                for (int i = 0; i < nRows; i++) {
                    for (int j = 0; j < nCols; j++) {
                        if (!coveredRows(i)) {
                            /* If the row is uncovered and the column is covered, 
                        then it's a no-op: add and subtract the same value.
                             */
                            if (!coveredCols(j)) {
                                costMatrix(i)(j) -= min;
                            }
                        } else if (coveredCols(j)) {
                            costMatrix(i)(j) += min;
                        }
                    }
                }
                //go back to step 4
                position = primeZero();
            }
            //perform step 5
            invertPrimedAndStarred(position);
        }
        //format the result
        int assignmentIndex = 0;
        if (transposed){
            for (int i = 0; i < nCols; i++){
                rowAssignments(i) = starredCols(i);
                if (starredCols(i) != -1){
                    assignments(assignmentIndex)(0) = starredCols(i);
                    assignments(assignmentIndex)(1) = i;
                    assignmentIndex++;
                }
            }
            System.arraycopy(starredRows, 0, colAssignments, 0, nRows);
        } else {
        for (int i = 0; i < nRows; i++){
                rowAssignments(i) = starredRows(i);
                if (starredRows(i) != -1) {
                    assignments(assignmentIndex)(0) = i;
                    assignments(assignmentIndex)(1) = starredRows(i);
                    assignmentIndex++;
                }
            }
            System.arraycopy(starredCols, 0, colAssignments, 0, nCols);
        }
    }

    /**
     * Primes uncovered zeroes in the cost matrix.
     * Performs the fourth step of the Hungarian Algorithm.
     * @return the (rowIndex,colIndex) coordinates of the primed zero to star 
     * that has been found, or null if no such zero has been found.
     */
    private int() primeZero() {
        Queue<Integer> uncoveredColumnQueue = new LinkedList<>();
        for (int i = 0; i < nRows; i++) {
            if (coveredRows(i)) {
                continue;
            }
            for (int j = 0; j < nCols; j++) {
                if (coveredCols(j) || costMatrix(i)(j) > 0) {
                    continue;
                }
                //Found a non-covered zero
                primedRows(i) = j;
                primedCols(j) = i;
                if (starredRows(i) == -1) {
                    return new int(){i,j};
                } else {
                    coveredRows(i) = true;
                    coveredCols(starredRows(i)) = false;
                    numberCoveredCols -= 1;
                    //ignore the rest of the row but handle the uncovered column
                    uncoveredColumnQueue.add(starredRows(i));
                    break;
                }
            }
        }
        while (!uncoveredColumnQueue.isEmpty()){
            int j = uncoveredColumnQueue.remove();
            for (int i = 0; i < nRows; i++){
                if(coveredRows(i) || costMatrix(i)(j) > 0) {
                    continue;
                }
                primedRows(i) = j;
                primedCols(j) = i;
                if (starredRows(i) == -1){
                    return new int(){i,j};
                } else {
                    coveredRows(i) = true;
                    coveredCols(starredRows(i)) = false;
                    numberCoveredCols -= 1;
                    uncoveredColumnQueue.add(starredRows(i));
                }
            }
        }
        return null;
    }
    
    /**
     * Stars selected primed zeroes to increase the line coverage of the matrix.
     * Performs the fifth step of the Hungarian Algorithm.
     * @param position array of size 2 containing the row and column indices of 
     * the first primed zero in the alternating series to modify.
     */
    private void invertPrimedAndStarred(int() position){
        int currentRow = position(0);
        int currentCol = position(1);
        int tmp;
        starredRows(currentRow) = currentCol;
        while (starredCols(currentCol) != -1){
            //Move star to its new row in the column of the primed zero
            tmp = starredCols(currentCol);
            starredCols(currentCol) = currentRow;
            currentRow = tmp;
            //Move star to its new column in the column of the previously 
            //starred zero
            tmp = primedRows(currentRow);
            starredRows(currentRow) = tmp;
            currentCol = tmp;
        }
        //set starredCols of last changed zero and reset primes and lines covering
        starredCols(currentCol) = currentRow;
        for (int i = 0; i < coveredRows.length; i++){
            coveredRows(i) = false;
            primedRows(i) = -1;
        }
        //in next step, all columns containing a starred zero will be marked
        //--> do it right away
        for (int j = 0; j < nCols; j++){
            if(!coveredCols(j) && starredCols(j) != -1){
                numberCoveredCols++;
                coveredCols(j) = true;
            }
            //if a column contained a prime zero, it will still contain one
            //after the inversion, so the case where a column needs to be 
            //uncovered does not arise
            primedCols(j) = -1;
        }
    }

    /**
     * @return The internal state of the cost matrix.
     */
    int()() getState() {
        return this.costMatrix;
    }

    /**
     * Checks the validity of the input cost matrix.
     * @param costMatrix the matrix to solve.
     * @throws IllegalArgumentException if {@code costMatrix } is not 
     * rectangular (e.g. rows do not all have the same length).
     */
    static void checkMatrixValidity(int()() costMatrix)
            throws IllegalArgumentException{
        if (costMatrix == null){
            throw new IllegalArgumentException("input matrix was null");
        }
        if (costMatrix.length == 0){
            throw new IllegalArgumentException("input matrix was of length 0");
        }
        for (int() row : costMatrix){
            if (row.length != costMatrix(0).length){
                throw new IllegalArgumentException("input matrix was not rectangular");
            }
        }
    }
}

TestArguments.java

package test.tools;

/**
 * Interface defining the general contract that inner classes should implement
 * to ease the unit testing.
 * 
 * Concrete classes implementing it should provide a unique constructor similar
 * to the main one of the class parameter, and override the toString object 
 * method.
 *
 * @param <T>   class under test: the arguments will be used to generate 
 * instances of that class.
 */
public interface TestArguments<T> {
    /**
     * Initialises an object to use in a test.
     * @return
     */
    T convert();
}

TestFrameWork.java

package test.tools;

import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.function.Executable;

public interface TestFrameWork<T, S extends TestArguments<T>> {
    /**
     * @return a {@link Stream} of arguments to initialise an object to test.
     */
    Stream<S> argumentsSupplier();
    
    default String testName(String methodName, S args){
        return String.format("%s.%s on %s", 
                this.getClass().getCanonicalName(), methodName, args);
    }
    /**
     * Forges a {@link DynamicTest} to run the input test for each element 
     * returned by the implementation of 
     * {@link TestFrameWork#argumentsSupplier()}.
     * @param methodName    to set as the test name.
     * @param tester        to run as the test.
     * @return  a stream of nodes running the test.
     */
    default Stream<DynamicTest> test(String methodName, Consumer<S> tester){
        return test(argumentsSupplier(), methodName, tester);
    }

    /**
     * Forges a {@link Stream} of {@link DynamicNode} that runs in independent
     * {@link DynamicTest} instances each {@link Executable} returned by the 
     * input {@link Function} on each element returned by the implementation of
     * {@link TestFrameWork#argumentsSupplier()}.
     * @param methodName    to set as the test container's name.
     * @param testerStream  to generate the {@link Stream} of test using for 
     * each element the {@link String} as a suffix in the test name and the
     * {@link Executable} as the test to run.
     * @return  a stream of nodes running the tests.
     */
    default Stream<DynamicNode> testContainer(String methodName, 
            Function<S, Stream<Map.Entry<String, Executable>>> testerStream){
        return testContainer(argumentsSupplier(), methodName, testerStream);
    }
    /**
     * Forges a {@link DynamicTest} to run the input test for each element 
     * of a {@link Stream} of arguments.
     * @param stream        of arguments, the tests will be run on each 
     * element.
     * @param methodName    to set as the test name.
     * @param tester        to run as the test.
     * @return  a stream of nodes running the tests.
     */
    default Stream<DynamicTest> test(Stream<S> stream, String methodName, Consumer<S> tester){
        return stream.map(args
                -> dynamicTest(testName(methodName, args), () -> tester.accept(args)));
    }
    /**
     * Forges a {@link Stream} of {@link DynamicNode} that runs in independent
     * {@link DynamicTest} instances each {@link Executable} returned by the 
     * input {@link Function} on each element of the input {@link Stream}.
     * @param stream        of arguments, the tests will be run on each 
     * element.
     * @param methodName    to set as the test container's name.
     * @param testerStream  to generate the {@link Stream} of test using for 
     * each element the {@link String} as a suffix in the test name and the
     * {@link Executable} as the test to run.
     * @return  a stream of nodes running the tests.
     */
    default Stream<DynamicNode> testContainer(Stream<S> stream, String methodName, 
            Function<S, Stream<Map.Entry<String, Executable>>> testerStream){
        return stream.map(args
                -> {
                    String message = testName(methodName, args);
                    return dynamicContainer(message, 
                            testerStream.apply(args).map(entry 
                                    -> dynamicTest(message + entry.getKey(), entry.getValue())));
                });
    }
}

HungarianSolverTest.java

package AssignmentProblem;

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import test.tools.TestArguments;
import test.tools.TestFrameWork;

public class HungarianSolverTest implements TestFrameWork<HungarianSolver, HungarianArgument> {

    @TestFactory
    public Stream<DynamicTest> testInitialiseValidInput() {
        //Check that initialise does not crash on valid input.
        //Correctness of the result is checked in tests linked to the methods getting the results.
        return test("initialise (valid input)", v -> v.convert());
    }
    
    @TestFactory
    public Stream<DynamicTest> testInitialiseInvalidInput(){
        Stream<HungarianArgument> cases = Stream.of(
                new HungarianArgument(null, null, null, null, "null cost matrix"),
                new HungarianArgument(new int(0)(0), null, null, null, "size 0 cost matrix"),
                new HungarianArgument(new int()(){{0}, {0,1}, {0,1,2},{0,1},{0}}, null, null, null, "non-rectangular cost matrix"));
        return test(cases, 
                "initialise (invalid input)", 
                v -> Assertions.assertThrows(IllegalArgumentException.class, 
                        () -> v.convert()));
    }

    @TestFactory
    public Stream<DynamicTest> testGetRowAssignments() {
        return test("getRowAssignments", v -> assertArrayEquals(v.expectedRowAssignment, v.convert().getRowAssignments()));
    }

    @TestFactory
    public Stream<DynamicTest> testGetColumnAssignemnts() {
        return test("getColumnAssignments", v -> assertArrayEquals(v.expectedColAssignment, v.convert().getColumnAssignemnts()));
    }

    @TestFactory
    public Stream<DynamicTest> testGetAssignments() {
        Comparator<int()> comparator = (first, second) ->
            Integer.compare(first(0), second(0)) == 0 ? Integer.compare(first(1), second(1)) : Integer.compare(first(0), second(0));
        return test("getAssignments", v-> {
            /*
            There is no contract on the ordering of the result values.
            */
            int()() assignments = v.convert().getAssignments();
            Arrays.sort(assignments, comparator);
            Arrays.sort(v.expectedMatrixResult, comparator);
            assertArrayEquals(v.expectedMatrixResult, assignments);
        });
    }

    @TestFactory
    public Stream<DynamicTest> testReduceInitialMatrix() {
        Stream<HungarianArgument> cases = Stream.of(
                new HungarianArgument(new int()(){{25, 40, 35}, {40, 60, 35}, {20, 40, 25}}, 
                        new int()(){{0, 0, 10}, {5, 10, 0}, {0, 5, 5}}, 
                        null, null, "square 3*3 matrix"),
                new HungarianArgument(new int()(){{150, 400, 450},{200, 600, 350}, {200, 400, 250}},
                        new int()(){{0, 50, 250}, {0, 200, 100}, {0, 0, 0}},
                        null, null, "second square 3*3 matrix"),
                new HungarianArgument(new int()(){{70, 40, 20, 55},{65, 60, 45, 90},{30, 45, 50, 75},{25,0,55,40}},
                        new int()(){{50, 20, 0, 0},{20, 15, 0, 10},{0, 15, 20, 10},{25, 0, 55, 5}},
                        null, null, "square 4*4 with initial zeroes matrix"),
                new HungarianArgument(new int()(){{1,2,25,13},{5,7,25,15},{10,13,16,13},{17,21,11,18},{15,15,15,14}},
                        new int()(){{0,2,9,16,13},{0,3,11,19,12},{14,12,5,0,3},{0,0,0,5,0}}, 
                        null, null, "5*4 matrix without initial zeroes")
        );
        return test(cases, 
                "reduceInitialMatrix", 
                v -> {
                    HungarianSolver solver = v.convertWithConstructor();
                    solver.reduceInitialMatrix();
                    assertArrayEquals(v.expectedMatrixResult, solver.getState());
                });
    }

    @Override
    public Stream<HungarianArgument> argumentsSupplier() {
        int worstCaseSize = 200;
        int()() worstCaseMatrix = new int(worstCaseSize)(worstCaseSize);
        for (int i = 0; i < worstCaseMatrix.length; i++) {
            for (int j = 0; j < worstCaseMatrix(i).length; j++){
                worstCaseMatrix(i)(j) = (i+1)*(j+1);
            }
        }
        int() worstCaseLinearExpectation = new int(worstCaseSize);
        Arrays.setAll(worstCaseLinearExpectation, i -> worstCaseSize-i-1);
        int()() worstCaseExpectedAssignments = new int(worstCaseSize)(2);
        for (int i = 0; i < worstCaseSize; i++){
            worstCaseExpectedAssignments(i)(0) = i;
            worstCaseExpectedAssignments(i)(1) = worstCaseSize-i-1;
        }
        return Stream.of(new HungarianArgument(new int()(){{2500, 4000, 3500}, {4000, 6000, 3500}, {2000, 4000, 2500}},
                new int()(){{0,1},{1,2},{2,0}}, new int(){1,2,0}, new int(){2,0,1}, "simple 3*3 matrix"),
                new HungarianArgument(new int()(){{2000,6000,3500},{1500, 4000, 4500},{2000,4000,2500}},
                new int()(){{0,0},{1,1},{2,2}}, new int(){0,1,2}, new int(){0,1,2}, "mildly complex 3*3 matrix"),
                new HungarianArgument(new int()(){{1,2,3,4},{5,6,7,8},{9,10,11,12}},
                new int()(){{0,0},{1,1},{2,2}}, new int(){0,1,2}, new int(){0,1,2,-1}, "complex 4*3 matrix with equality case"),
                new HungarianArgument(new int()(){{1,2,25,13},{5,7,25,15},{10,13,16,13},{17,21,11,18},{15,15,15,14}},
                new int()(){{0,1},{1,0},{2,3},{3,2}}, new int(){1,0,3,2,-1}, new int(){1,0,3,2}, "first complex 5*4 matrix without equality case"),
                new HungarianArgument(new int()(){{1,2,25,13},{5,7,25,15},{10,13,16,14},{17,21,11,18},{15,15,15,13}},
                new int()(){{0,1},{1,0},{2,3},{3,2}}, new int(){1,0,3,2,-1}, new int(){1,0,3,2}, "second complex 5*4 matrix without equality case"),
                new HungarianArgument(worstCaseMatrix, worstCaseExpectedAssignments,
                worstCaseLinearExpectation, worstCaseLinearExpectation, "worst case " + worstCaseSize + "*" + worstCaseSize + " matrix")
        );
    }
    
}

class HungarianArgument implements TestArguments<HungarianSolver>{
    final int()() costMatrix;
    final int()() expectedMatrixResult;
    final int() expectedRowAssignment;
    final int() expectedColAssignment;
    private final String name;
    HungarianArgument(int()() costMatrix, int()() expectedMatrixResult, 
            int() expectedRowAssignment, int() expectedColAssignment,
            String name){
        this.costMatrix = costMatrix;
        this.expectedMatrixResult = expectedMatrixResult;
        this.expectedRowAssignment = expectedRowAssignment;
        this.expectedColAssignment = expectedColAssignment;
        this.name = name;
    }
    @Override
    public HungarianSolver convert() {
        return HungarianSolver.initialise(costMatrix);
    }
    public HungarianSolver convertWithConstructor(){
        return new HungarianSolver(costMatrix);
    }
    
    @Override
    public String toString(){
        return this.name;
    }
}

combinatorics – Can I use the Hungarian algorithm to arrange n cards to form a given string?

Suppose you’re given a string s that consists of lowercase alphabetic letters only. The length of the sentence is n. You are given n cards, which has lowercase alphabetic letters on the front and back. You want to arrange (shuffle and/or flip each card) the n cards in the order that produces the sentence. You can use 1 of 2 sides of a card, but obviously not both.

I am wondering if this problem can be converted to a Hungarian Algorithm problem? We essentially have a bipartite here. The left graph consists of n nodes represent each character in s. For each character in s, there is a subset of the n cards that can can be used to create that character.

Can this be turned into a problem that can be solved with the Hungarian algo?

hungary – Received Fine from Hungarian Police even though I had bought Vignette at the Hegyeshalom Border Crossing

I had rented the car in Vienna and did a day trip to Budapest. I had bought the Vignette at the Hegyeshalom Border Crossing (on the way from Vienna to Budapest).
I have now email from my Rental Car Company about fine from Hungary Department for driving without Vignette.
I don’t have a proof document now, and it was paid by cash (as they didn’t had pay by card option at that shop).
How can I find out the phone number of the agency who sold me ticket?
Any help in greatly appreciated.

driving – As a Hungarian national, can I enter the Schengen area through Austria, while traveling from Tanzania?

I plan on traveling to Zanzibar, Tanzania. I would drive from Budapest to Vienna, then fly from there though either Doha or Dubai. As far as I know, this part of my itinerary would be difficulty-free, at least when it comes to coronavirus-related border closures and travel restrictions.

However, I’m not sure whether the same can be said about the return journey. I have been browsing this site for general information about the restrictions. I haven’t found the answer.

The question: is it possible for me to travel to Austria from Tanzania? The site doesn’t list my African destination in either the “open borders” or the “high-risk countries”, so I’m not sure whether it’s possible at all.

Thanks.

driving – As a Hungarian national, can I enter the Schengen area through Austria without a test, while traveling from Tanzania?

I plan on traveling to Zanzibar, Tanzania. I would drive from Budapest to Vienna, then fly from there though either Doha or Dubai. As far as I know, this part of my itinerary would be difficulty-free, at least when it comes to coronavirus-related border closures and travel restrictions.

However, I’m not sure whether the same can be said about the return journey. I have been browsing this site for general information about the restrictions.

The first part of my question: is it possible for me to travel to Austria from Tanzania? The site doesn’t list my African destination in either the “open borders” or the “high-risk countries”, so I’m not sure whether it’s possible at all.

The second part: The above site confirms it is possible to skip the testing/quarantine in Austria if you are only transiting through the country. Since I plan on driving back to Budapest immediately after arriving from Zanzibar, this condition will be met. However, the form I will have to fill clearly says that I will need to produce some evidence, such as ticket, taxi booking, etc., to prove that I actually intend to leave the country. Since I will be driving my own car, there will be no such document. Could it be a problem?

Thanks.

naming – System Hungarian Notation for Android UI components?

“Hungarian notation” can be useful to include additional information in the variable name, when that information cannot be represented in the type system. However, Systems Hungarian is entirely pointless and merely duplicates information that is already available. Most occurrences of Systems Hungarian are legacy code, or a misunderstanding of the benefits of Hungarian notation.

For example, I might use a variable like startCountdownButton in untyped languages like Python, but probably not in statically typed languages like Java. Depending on your architecture (like MVC, MVVM), there is also unlikely to be confusion between a button and an action because they aren’t part of the same object.

Nevertheless, if you find that including a prefix or suffix in your variable names makes them clearer in the context of your code, then go for it. There are many opinions on whether or not to use Hungarian notation, and some of them are phrased very strongly (you should never do this, always do that). But they are just opinions and guidelines, no strict laws. Do what makes sense for your code.

legal – Told Hungarian border police I walked along the outer Schengen border and took photos of it – will I be fined?

I hold an EU passport of a Schengen area member state and travelled on a day trip to Hungary. In Hungary, I walked to and then along the outer Schengen border until I reached a border crossing, where I ended up between the border checkpoints of the two neighbouring countries. During my walk along the green border (i.e. a grass strip, without any fences or other barriers), I took a large number of photos of the grass strip and border markings (stones).

Believing that I had done nothing illegal, I went to the Hungarian border checkpoint and explained that I had not left the Schengen area and was legally allowed to be in Hungary. To prove this, I showed the border police officers a photo of the grass strip I walked on and a border marker, which showed that the grass strip was still inside the Schengen area. I had to hand over my phone, and the border police officers went through all the photos I took that day, then I was instructed to irrecoverably delete all my photos from the border area.

This was during Coronavirus travel restrictions – I was allowed to cross the border between Hungary and my home country, but apparently travel between Hungary and the non-Schengen neighbouring country was still generally prohobited. Travel to the non-Schengen country would not normally require a visa for me.

I was eventually allowed to pass the border checkpoint, but one of the border police officers told me that I will face a hefty fine for the photos I took, with a friendly smile that made me unsure whether he was serious or just joking. Another border police agent told me that being in the immediate vicinity of the outer Schengen border is prohibited by itself, but none of them seemed to know the exact legal situation particularly well.

What consequences should I fear now?