Cod sursa(job #1741692)

Utilizator AlexandruValeanuAlexandru Valeanu AlexandruValeanu Data 14 august 2016 18:56:29
Problema Cuplaj maxim de cost minim Scor 100
Compilator java Status done
Runda Arhiva educationala Marime 8.94 kb
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        InputReader in = new InputReader(new FileInputStream("cmcm.in"));

        int N = in.nextInt();
        int M = in.nextInt();
        int E = in.nextInt();

        int index[][] = new int[N][M];
        double[][] costMatrix = new double[N][M];

        final int INFINITY = 30000;

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                costMatrix[i][j] = INFINITY * INFINITY;
            }
        }

        for (int i = 0; i < E; i++) {
            int x = in.nextInt() - 1;
            int y = in.nextInt() - 1;
            int w = in.nextInt();

            index[x][y] = i + 1;
            costMatrix[x][y] = INFINITY + w;
        }

        HungarianAlgorithm HA = new HungarianAlgorithm(costMatrix);
        int[] answer = HA.execute();

        int totalCost = 0;
        int matching = 0;

        for (int i = 0; i < answer.length; i++) {
            if (answer[i] != -1 && costMatrix[i][answer[i]] != INFINITY * INFINITY) {
                totalCost += costMatrix[i][answer[i]] - INFINITY;
                matching++;
            }
        }
        
        PrintWriter out = new PrintWriter(new FileOutputStream("cmcm.out"));

        out.println(matching + " " + totalCost);

        for (int i = 0; i < answer.length; i++)
            if (answer[i] != -1 && costMatrix[i][answer[i]] != INFINITY * INFINITY)
                out.print(index[i][answer[i]] + " ");

        out.println();
        out.close();
    }

    static class HungarianAlgorithm {
        private final double[][] costMatrix;
        private final int N, rows, columns;

        private final double[] labelByWorker, labelByJob;
        private final int[] matchJobByWorker, matchWorkerByJob;

        private final boolean[] committedWorkers;
        private final int[] workerByCommittedJob;

        private final double[] minSlackValueByJob;
        private final int[] minSlackWorkerByJob;

        HungarianAlgorithm(double[][] costMatrix){
            this.N = Math.max(costMatrix.length, costMatrix[0].length);
            this.rows = costMatrix.length;
            this.columns = costMatrix[0].length;
            this.costMatrix = new double[this.N][this.N];

            for (int w = 0; w < this.N; w++)
                if (w < costMatrix.length){
                    if (costMatrix[w].length != this.columns)
                        throw new IllegalArgumentException("Irregular cost matrix!");

                    this.costMatrix[w] = Arrays.copyOf(costMatrix[w], this.N);
                }
                else
                    this.costMatrix[w] = new double[this.N];

            labelByWorker = new double[this.N];
            labelByJob = new double[this.N];

            committedWorkers = new boolean[this.N];
            workerByCommittedJob = new int[this.N];

            minSlackValueByJob = new double[this.N];
            minSlackWorkerByJob = new int[this.N];

            matchJobByWorker = new int[this.N];
            Arrays.fill(matchJobByWorker, -1);
            matchWorkerByJob = new int[this.N];
            Arrays.fill(matchWorkerByJob, -1);
        }

        private void reduce(){
            for (int w = 0; w < N; w++) {
                double min = Double.POSITIVE_INFINITY;

                for (int j = 0; j < N; j++)
                    min = Math.min(min, costMatrix[w][j]);

                for (int j = 0; j < N; j++)
                    costMatrix[w][j] -= min;
            }

            double[] min = new double[N];
            Arrays.fill(min, Double.POSITIVE_INFINITY);

            for (int w = 0; w < N; w++)
                for (int j = 0; j < N; j++)
                    min[j] = Math.min(min[j], costMatrix[w][j]);

            for (int w = 0; w < N; w++)
                for (int j = 0; j < N; j++)
                    costMatrix[w][j] -= min[j];
        }

        private void computeInitialFeasibleSolution(){
            Arrays.fill(labelByJob, Double.POSITIVE_INFINITY);

            for (int w = 0; w < N; w++)
                for (int j = 0; j < N; j++)
                    labelByJob[j] = Math.min(labelByJob[j], costMatrix[w][j]);
        }

        private void match(int w, int j){
            matchJobByWorker[w] = j;
            matchWorkerByJob[j] = w;
        }

        private void greedyMatching(){
            for (int w = 0; w < N; w++)
                for (int j = 0; j < N; j++)
                    if (matchJobByWorker[w] == -1 && matchWorkerByJob[j] == -1 &&
                            costMatrix[w][j] - labelByJob[j] - labelByWorker[w] == 0)
                        match(w, j);
        }

        private int getUnmatchedWorker(){
            int w = 0;

            while (w < N && matchJobByWorker[w] != -1)
                w++;

            return w;
        }

        private void updateLabeling(double slack){
            for (int w = 0; w < N; w++)
                if (committedWorkers[w])
                    labelByWorker[w] += slack;

            for (int j = 0; j < N; j++) {
                if (workerByCommittedJob[j] != -1)
                    labelByJob[j] -= slack;
                else
                    minSlackValueByJob[j] -= slack;
            }
        }

        private void initializePhase(int w){
            Arrays.fill(committedWorkers, false);
            Arrays.fill(workerByCommittedJob, -1);

            committedWorkers[w] = true;

            for (int j = 0; j < N; j++) {
                minSlackValueByJob[j] = costMatrix[w][j] - labelByJob[j] - labelByWorker[w];
                minSlackWorkerByJob[j] = w;
            }
        }

        private void executePhase(){
            while (true){
                int minSlackWorker = -1, minSlackJob = -1;
                double minSlackValue = Double.POSITIVE_INFINITY;

                for (int j = 0; j < N; j++)
                    if (workerByCommittedJob[j] == -1){
                        if (minSlackValueByJob[j] < minSlackValue){
                            minSlackValue = minSlackValueByJob[j];
                            minSlackWorker = minSlackWorkerByJob[j];
                            minSlackJob = j;
                        }
                    }

                if (minSlackValue > 0)
                    updateLabeling(minSlackValue);

                workerByCommittedJob[minSlackJob] = minSlackWorker;

                if (matchWorkerByJob[minSlackJob] == -1){
                    int committedJob = minSlackJob;
                    int worker = workerByCommittedJob[committedJob];

                    while (true){
                        int tmp = matchJobByWorker[worker];
                        match(worker, committedJob);
                        committedJob = tmp;

                        if (committedJob == -1)
                            break;

                        worker = workerByCommittedJob[committedJob];
                    }

                    return;
                }
                else{
                    int worker = matchWorkerByJob[minSlackJob];
                    committedWorkers[worker] = true;

                    for (int j = 0; j < N; j++)
                        if (workerByCommittedJob[j] == -1){
                            double slack = costMatrix[worker][j] - labelByWorker[worker] - labelByJob[j];

                            if (minSlackValueByJob[j] > slack){
                                minSlackValueByJob[j] = slack;
                                minSlackWorkerByJob[j] = worker;
                            }
                        }
                }
            }
        }

        public int[] execute(){
            reduce();
            computeInitialFeasibleSolution();
            greedyMatching();

            int w = getUnmatchedWorker();
            while (w < N){
                initializePhase(w);
                executePhase();
                w = getUnmatchedWorker();
            }

            int[] answer = Arrays.copyOf(matchJobByWorker, this.rows);

            for (w = 0; w < answer.length; w++)
                if (answer[w] >= this.columns)
                    answer[w] = -1;

            return answer;
        }
    }


    static class InputReader{
        private BufferedReader reader;
        private StringTokenizer tokenizer;

        InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 65536);
            tokenizer = null;
        }

        private String nextToken() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()){
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                }
                catch (IOException e){
                    throw new RuntimeException(e);
                }
            }

            return  tokenizer.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(nextToken());
        }

        String nextString(){
            return nextToken();
        }
    }
}