匈牙利匹配

长腿白菜
长腿白菜
发布于 2025-02-11 / 10 阅读
0
0

匈牙利匹配

一个可视化展示匈牙利匹配算法的HTML代码。

<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 900px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .matrix {
            display: inline-block;
            margin: 20px;
            background: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .cell {
            width: 60px;
            height: 60px;
            border: 2px solid #e0e0e0;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            margin: 3px;
            font-weight: bold;
            font-size: 18px;
            transition: all 0.3s;
            border-radius: 4px;
            position: relative;
        }
        .cell.selected {
            background-color: #a8e6cf;
            transform: scale(1.05);
        }
        .cell.min-row {
            background-color: #ffd3b6;
        }
        .cell.min-col {
            background-color: #ffaaa5;
        }
        .cell.matched {
            background-color: #dcedc1;
            transform: scale(1.05);
        }
        .cell .original {
            position: absolute;
            top: 2px;
            right: 2px;
            font-size: 10px;
            color: #999;
        }
        button {
            padding: 12px 24px;
            margin: 10px;
            font-size: 16px;
            cursor: pointer;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            transition: all 0.3s;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        button:hover {
            background-color: #45a049;
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
        }
        button:active {
            transform: translateY(0);
        }
        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
        .controls {
            margin: 20px 0;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .explanation {
            margin: 20px 0;
            padding: 15px;
            background-color: #f8f9fa;
            border-left: 4px solid #4CAF50;
            border-radius: 4px;
            line-height: 1.6;
        }
        .matrices-container {
            display: flex;
            justify-content: center;
            align-items: center;
            flex-wrap: wrap;
            gap: 20px;
        }
        .matrix-label {
            font-weight: bold;
            text-align: center;
            margin-bottom: 10px;
            color: #333;
        }
        .step-counter {
            text-align: center;
            font-size: 18px;
            color: #666;
            margin: 10px 0;
        }
        .highlight {
            animation: highlight 1s ease-in-out;
        }
        @keyframes highlight {
            0% { transform: scale(1); }
            50% { transform: scale(1.1); }
            100% { transform: scale(1); }
        }
    </style>
</head>
<body>
    <div class="container">
        <h2 style="text-align: center; color: #333;">匈牙利算法可视化演示</h2>
        <div class="controls">
            <button onclick="randomizeMatrix()" id="randomBtn">随机初始化</button>
            <button onclick="resetMatrix()" id="resetBtn">重置矩阵</button>
            <button onclick="nextStep()" id="nextBtn">下一步</button>
            <button onclick="autoPlay()" id="autoPlayBtn">自动演示</button>
        </div>
        <div class="step-counter" id="stepCounter">步骤: 0/3</div>
        <div class="matrices-container">
            <div>
                <div class="matrix-label">原始矩阵</div>
                <div id="originalMatrix" class="matrix"></div>
            </div>
            <div>
                <div class="matrix-label">当前矩阵</div>
                <div id="matrix" class="matrix"></div>
            </div>
        </div>
        <div id="explanation" class="explanation"></div>
    </div>

    <script>
        let currentStep = 0;
        let matrix = [
            [3, 2, 3],
            [2, 3, 2],
            [3, 2, 2]
        ];
        let originalMatrix = [...matrix.map(row => [...row])];
        let autoPlayInterval;

        function randomizeMatrix() {
            stopAutoPlay();
            matrix = Array(3).fill().map(() => 
                Array(3).fill().map(() => Math.floor(Math.random() * 9) + 1)
            );
            originalMatrix = [...matrix.map(row => [...row])];
            currentStep = 0;
            updateDisplay();
            updateExplanation('随机初始化的代价矩阵。数字范围:1-9');
        }

        function createMatrixDisplay(matrixData, containerId, showOriginal = false) {
            const matrixDiv = document.getElementById(containerId);
            matrixDiv.innerHTML = '';
            for(let i = 0; i < matrixData.length; i++) {
                for(let j = 0; j < matrixData[i].length; j++) {
                    const cell = document.createElement('div');
                    cell.className = 'cell';
                    cell.textContent = matrixData[i][j];
                    if(showOriginal && containerId === 'matrix') {
                        const original = document.createElement('span');
                        original.className = 'original';
                        original.textContent = originalMatrix[i][j];
                        cell.appendChild(original);
                    }
                    cell.id = `${containerId}-cell-${i}-${j}`;
                    matrixDiv.appendChild(cell);
                }
                matrixDiv.appendChild(document.createElement('br'));
            }
        }

        function updateDisplay() {
            createMatrixDisplay(originalMatrix, 'originalMatrix');
            createMatrixDisplay(matrix, 'matrix', true);
            document.getElementById('stepCounter').textContent = `步骤: ${currentStep}/3`;
        }

        function updateExplanation(text) {
            const explanation = document.getElementById('explanation');
            explanation.textContent = text;
            explanation.classList.remove('highlight');
            void explanation.offsetWidth; // 触发重绘
            explanation.classList.add('highlight');
        }

        function resetMatrix() {
            stopAutoPlay();
            currentStep = 0;
            matrix = originalMatrix.map(row => [...row]);
            updateDisplay();
            updateExplanation('初始代价矩阵。数字表示分配成本(越小越好)。');
        }

        function findMinInRow(rowIndex) {
            return Math.min(...matrix[rowIndex]);
        }

        function findMinInColumn(colIndex) {
            return Math.min(...matrix.map(row => row[colIndex]));
        }

        function autoPlay() {
            const autoPlayBtn = document.getElementById('autoPlayBtn');
            if(autoPlayBtn.textContent === '自动演示') {
                autoPlayBtn.textContent = '停止演示';
                autoPlayInterval = setInterval(() => {
                    if(currentStep >= 3) {
                        resetMatrix();
                    }
                    nextStep();
                }, 2000);
            } else {
                stopAutoPlay();
            }
        }

        function stopAutoPlay() {
            clearInterval(autoPlayInterval);
            document.getElementById('autoPlayBtn').textContent = '自动演示';
        }

        function nextStep() {
            currentStep++;
            switch(currentStep) {
                case 1:
                    // 行归约
                    for(let i = 0; i < matrix.length; i++) {
                        const minVal = findMinInRow(i);
                        for(let j = 0; j < matrix[i].length; j++) {
                            matrix[i][j] -= minVal;
                        }
                    }
                    updateDisplay();
                    updateExplanation('步骤1:行归约 - 每行减去该行最小值,使每行至少有一个0');
                    break;

                case 2:
                    // 列归约
                    for(let j = 0; j < matrix[0].length; j++) {
                        const minVal = findMinInColumn(j);
                        for(let i = 0; i < matrix.length; i++) {
                            matrix[i][j] -= minVal;
                        }
                    }
                    updateDisplay();
                    updateExplanation('步骤2:列归约 - 每列减去该列最小值,使每列至少有一个0');
                    break;

                case 3:
                    // 显示匹配
                    let zeros = findZeros();
                    zeros.forEach(([i, j]) => {
                        document.getElementById(`matrix-cell-${i}-${j}`).classList.add('matched');
                    });
                    updateExplanation('步骤3:找到最优匹配(绿色标记)。每行/列只能选择一个0值,表示最优分配方案。');
                    break;

                default:
                    resetMatrix();
                    break;
            }
        }

        function findZeros() {
            let matched = [];
            let usedRows = new Set();
            let usedCols = new Set();

            // 查找所有的0
            for(let i = 0; i < matrix.length; i++) {
                for(let j = 0; j < matrix[i].length; j++) {
                    if(matrix[i][j] === 0 && !usedRows.has(i) && !usedCols.has(j)) {
                        matched.push([i, j]);
                        usedRows.add(i);
                        usedCols.add(j);
                    }
                }
            }
            return matched;
        }

        // 初始化显示
        updateDisplay();
        updateExplanation('初始代价矩阵。数字表示分配成本(越小越好)。');
    </script>
</body>
</html>


评论