一个可视化展示匈牙利匹配算法的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>