重走“长安的荔枝”路线,荔枝千里,智算万里

举报
不惑 发表于 2025/06/25 13:13:02 2025/06/25
【摘要】 唐朝天宝十四年,长安城一片繁华景象。唐玄宗对杨贵妃宠爱至极,听说岭南的荔枝鲜美无比,便想要在贵妃生日(六月初一)那天,让她吃上刚从枝头摘下的新鲜荔枝。要知道,在那个交通靠走,通讯靠吼的古代,这基本个不可能的任务!荔枝离开树枝,三天就变色变味。岭南到长安,足足五千里,山高路远。大臣们都知道这是个烫手山芋,谁接谁倒霉。倒霉蛋叫李善德,一个在长安混了二十多年、刚借钱买了小房子的九品芝麻官。他被同僚...

唐朝天宝十四年,长安城一片繁华景象。唐玄宗对杨贵妃宠爱至极,听说岭南的荔枝鲜美无比,便想要在贵妃生日(六月初一)那天,让她吃上刚从枝头摘下的新鲜荔枝。

要知道,在那个交通靠走,通讯靠吼的古代,这基本个不可能的任务!荔枝离开树枝,三天就变色变味。岭南到长安,足足五千里,山高路远。大臣们都知道这是个烫手山芋,谁接谁倒霉。倒霉蛋叫李善德,一个在长安混了二十多年、刚借钱买了小房子的九品芝麻官。他被同僚算计,稀里糊涂接下了这个“荔枝使”的差事。这哪是美差?分明是催命符!他吓得差点直接准备后事。但李善德没放弃。在朋友鼓励下,他抱着最后一丝希望,拼了命赶往岭南。在那里,他遇到了精明的胡商苏谅和种荔枝的姑娘阿僮。李善德发挥自己精于计算的长处,疯狂实验保鲜方法,设计严密的运输路线和接力方案,动用了驿站系统、快马、骑手,甚至耗费巨资购买冰块。为了这两坛小小的荔枝,沿途累死了不知多少匹马,跑垮了多少健壮的骑手,耗尽了地方多少财力物力。李善德夹在皇命和残酷现实之间,到处求人、受气,甚至得罪了朋友。

终于在贵妃生日当天,一骑烟尘冲入长安城,带来了勉强还算新鲜的荔枝。贵妃笑了,李善德的“任务”完成了。但亲眼目睹这巨大代价的李善德,内心无法平静。这奢靡的“一骑红尘妃子笑”背后,是无数普通人的血汗甚至生命。他最终选择了良知,代价是被流放岭南。讽刺的是,第二年安史之乱爆发,长安沦陷,远离权力中心的李善德反而躲过了战火。

这个浪漫而又惊心动魄的故事,正是我们这次路径优化项目的灵感来源。试想一下,如果我们用现代的算法和可视化技术,是否能在当年那条“荔枝道”上,找出一条真正意义上的最优路径?

城市图建模

从深圳出发,送抵西安,找出一条时间或费用最优的现代运输路线。根据提供的数据,我们构建了一张简化的城市图结构,每条道路都标注了运输数据,括号中的数字代表运输时间(单位:小时),我们可以将这张图理解为现代版“荔枝驿路”的抽象表达。每一段线路,不再是骑马与驿卒的奔波,而是运输车、物流网与算法智慧的协同决策。

city_graph = {

'深圳': {'广州': 1.5, '东莞': 1.0},
'广州': {'深圳': 1.5, '韶关': 2.5, '长沙': 5.5},
'东莞': {'深圳': 1.0, '惠州': 1.2},
'惠州': {'东莞': 1.2, '武汉': 8.0},

'韶关': {'广州': 2.5, '长沙': 4.0},
'长沙': {'韶关': 4.0, '武汉': 3.0, '郑州': 8.0},
'武汉': {'惠州': 8.0, '长沙': 3.0, '郑州': 4.5, '西安': 10.0},

'郑州': {'长沙': 8.0, '武汉': 4.5, '洛阳': 2.0},
'洛阳': {'郑州': 2.0, '西安': 5.0},
'西安': {'武汉': 10.0, '洛阳': 5.0}
}

算法选择

在如何走得更快这个问题上,古代靠人力和马匹,我们则靠算法。我们引入了三种现代路径搜索算法:

  1. Dijkstra算法: 找出从起点到终点的最短时间路径,适用于只考虑时间最优的场景。

  2. A Star 算法: 使用启发函数(如地理坐标距离)引导搜索,速度更快,方向更明确,就像驿使心中早已有图。

  3. 双目标加权算法: 同时考虑时间与费用,根据用户设定的权重系数,灵活决定是要快,还是要省,亦或二者兼顾。

多目标系统

在系统设计上,我们特别实现了一个灵活的双目标决策系统:

  • 时间权重(如0.7):代表任务对速度的敏感程度

  • 费用权重(如0.3):代表对成本的重视程度

  • 二者总和固定为1,用户可拖动滑块动态调整,系统即时计算

比如在“荔枝必须三日内送达”这种高度时效场景下,用户可能将时间权重拉高至 0.9;而在日常物流配送中,则可以更侧重费用优化。每一次调整,系统都会自动重新计算所有可能路径,并在地图上直观展示结果。

可视化界面

为了让技术结果更具直观性,我们用 HTML + JavaScript 构建了交互式路径地图,用户点击起点与终点,拖动权重滑块,点击“运行算法”,即可实时看到哪一条路线最合适,不论是为了“贵妃一笑”还是“成本最省”。主要包括:

  • SVG 城市图谱:展示所有节点与路径连接

  • 动态路径高亮:最优路线将沿图路径流动,犹如“荔枝专列”横穿中国

  • 节点悬停提示:显示每个城市的运输详情

  • 路径动画:模拟荔枝由南向北奔袭的过程

路线分析报告

系统在路径计算完成后,还会生成一份详细分析报告,包括:

现代技术,古人心愿

当我们借助 Dijkstra、A* 与加权算法追寻一条条最优路径时,仿佛也参与了一场与古人的运输任务。不同的是,古人靠的是驿站与人力,而我们,靠的是算力与数据。“长安的荔枝”,不再是千里奔袭下的奢侈与浪漫,而是数字化时代路径优化的现实案例。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能路径规划系统</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Microsoft YaHei', Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }

        .header p {
            font-size: 1.1em;
            opacity: 0.9;
        }

        .main-content {
            display: grid;
            grid-template-columns: 1fr 2fr;
            gap: 30px;
            padding: 30px;
        }

        .control-panel {
            background: #f8f9fa;
            padding: 25px;
            border-radius: 10px;
            height: fit-content;
        }

        .section {
            margin-bottom: 25px;
        }

        .section h3 {
            color: #333;
            margin-bottom: 15px;
            font-size: 1.2em;
            border-bottom: 2px solid #4facfe;
            padding-bottom: 5px;
        }

        .form-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #555;
        }

        select, input[type="range"], button {
            width: 100%;
            padding: 10px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 14px;
        }

        select:focus, input:focus {
            outline: none;
            border-color: #4facfe;
        }

        .weight-control {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .weight-control input {
            flex: 1;
        }

        .weight-value {
            min-width: 40px;
            text-align: center;
            font-weight: bold;
            color: #4facfe;
        }

        button {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
            border: none;
            cursor: pointer;
            font-weight: bold;
            transition: transform 0.2s;
        }

        button:hover {
            transform: translateY(-2px);
        }

        .algorithm-buttons {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
        }

        .map-container {
            background: white;
            border-radius: 10px;
            border: 2px solid #eee;
            position: relative;
            height: 600px;
            overflow: hidden;
        }

        .map-svg {
            width: 100%;
            height: 100%;
        }

        .city-node {
            fill: #4facfe;
            stroke: white;
            stroke-width: 3;
            cursor: pointer;
            transition: all 0.3s;
        }

        .city-node:hover {
            fill: #ff6b6b;
            transform: scale(1.2);
        }

        .city-label {
            font-size: 12px;
            font-weight: bold;
            fill: #333;
            text-anchor: middle;
            pointer-events: none;
        }

        .edge {
            stroke: #ccc;
            stroke-width: 2;
            fill: none;
        }

        .edge-label {
            font-size: 10px;
            fill: #666;
            text-anchor: middle;
        }

        .path-edge {
            stroke: #ff6b6b;
            stroke-width: 4;
            animation: pathAnimation 2s ease-in-out;
        }

        @keyframes pathAnimation {
            0% { stroke-dasharray: 1000; stroke-dashoffset: 1000; }
            100% { stroke-dasharray: 1000; stroke-dashoffset: 0; }
        }

        .results {
            margin-top: 20px;
            padding: 20px;
            background: #e8f5e8;
            border-radius: 10px;
            border-left: 5px solid #28a745;
        }

        .results h4 {
            color: #28a745;
            margin-bottom: 10px;
        }

        .path-info {
            margin-bottom: 15px;
        }

        .path-steps {
            background: white;
            padding: 15px;
            border-radius: 5px;
            margin-top: 10px;
        }

        .step {
            display: flex;
            justify-content: space-between;
            padding: 5px 0;
            border-bottom: 1px solid #eee;
        }

        .step:last-child {
            border-bottom: none;
        }

        .comparison-table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }

        .comparison-table th,
        .comparison-table td {
            padding: 10px;
            text-align: center;
            border: 1px solid #ddd;
        }

        .comparison-table th {
            background: #4facfe;
            color: white;
        }

        .best-result {
            background: #d4edda !important;
            font-weight: bold;
        }

        @media (max-width: 1024px) {
            .main-content {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header class="header">
            <h1>🗺️ 智能路径规划系统</h1>
            <p>多算法路径优化 | 双目标决策 | 可视化展示</p>
        </header>

        <div class="main-content">
            <div class="control-panel">
                <div class="section">
                    <h3>🎯 路径设置</h3>
                    <div class="form-group">
                        <label for="startCity">起始城市:</label>
                        <select id="startCity">
                            <option value="深圳" selected>深圳</option>
                            <option value="广州">广州</option>
                            <option value="东莞">东莞</option>
                            <option value="惠州">惠州</option>
                            <option value="韶关">韶关</option>
                            <option value="长沙">长沙</option>
                            <option value="武汉">武汉</option>
                            <option value="郑州">郑州</option>
                            <option value="洛阳">洛阳</option>
                            <option value="西安">西安</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="endCity">目标城市:</label>
                        <select id="endCity">
                            <option value="深圳">深圳</option>
                            <option value="广州">广州</option>
                            <option value="东莞">东莞</option>
                            <option value="惠州">惠州</option>
                            <option value="韶关">韶关</option>
                            <option value="长沙">长沙</option>
                            <option value="武汉">武汉</option>
                            <option value="郑州">郑州</option>
                            <option value="洛阳">洛阳</option>
                            <option value="西安" selected>西安</option>
                        </select>
                    </div>
                </div>

                <div class="section">
                    <h3>⚖️ 优化权重</h3>
                    <div class="form-group">
                        <label>时间权重: <span id="timeWeightValue">0.5</span></label>
                        <div class="weight-control">
                            <input type="range" id="timeWeight" min="0" max="1" step="0.1" value="0.5">
                        </div>
                    </div>
                    <div class="form-group">
                        <label>费用权重: <span id="costWeightValue">0.5</span></label>
                        <div class="weight-control">
                            <input type="range" id="costWeight" min="0" max="1" step="0.1" value="0.5">
                        </div>
                    </div>
                </div>

                <div class="section">
                    <h3>🔍 算法选择</h3>
                    <div class="algorithm-buttons">
                        <button onclick="runAlgorithm('dijkstra')">Dijkstra</button>
                        <button onclick="runAlgorithm('astar')">A* 算法</button>
                        <button onclick="runAlgorithm('weighted')">加权优化</button>
                        <button onclick="compareAlgorithms()">算法对比</button>
                    </div>
                </div>

                <div id="results" class="results" style="display: none;">
                    <h4>📊 计算结果</h4>
                    <div id="resultContent"></div>
                </div>
            </div>

            <div class="map-container">
                <svg class="map-svg" id="mapSvg" viewBox="0 0 800 600">
                    <!-- 地图将由JavaScript动态生成 -->
                </svg>
            </div>
        </div>
    </div>

    <script>
        // 城市数据:包含坐标、时间和费用信息
        const cityData = {
            '深圳': { x: 150, y: 500, connections: { '广州': { time: 1.5, cost: 120 }, '东莞': { time: 1.0, cost: 80 } } },
            '广州': { x: 100, y: 450, connections: { '深圳': { time: 1.5, cost: 120 }, '韶关': { time: 2.5, cost: 200 }, '长沙': { time: 5.5, cost: 450 } } },
            '东莞': { x: 200, y: 480, connections: { '深圳': { time: 1.0, cost: 80 }, '惠州': { time: 1.2, cost: 100 } } },
            '惠州': { x: 250, y: 450, connections: { '东莞': { time: 1.2, cost: 100 }, '武汉': { time: 8.0, cost: 600 } } },
            '韶关': { x: 150, y: 350, connections: { '广州': { time: 2.5, cost: 200 }, '长沙': { time: 4.0, cost: 320 } } },
            '长沙': { x: 300, y: 300, connections: { '韶关': { time: 4.0, cost: 320 }, '武汉': { time: 3.0, cost: 240 }, '郑州': { time: 8.0, cost: 640 } } },
            '武汉': { x: 400, y: 250, connections: { '惠州': { time: 8.0, cost: 600 }, '长沙': { time: 3.0, cost: 240 }, '郑州': { time: 4.5, cost: 360 }, '西安': { time: 10.0, cost: 800 } } },
            '郑州': { x: 500, y: 200, connections: { '长沙': { time: 8.0, cost: 640 }, '武汉': { time: 4.5, cost: 360 }, '洛阳': { time: 2.0, cost: 160 } } },
            '洛阳': { x: 550, y: 150, connections: { '郑州': { time: 2.0, cost: 160 }, '西安': { time: 5.0, cost: 400 } } },
            '西安': { x: 600, y: 100, connections: { '武汉': { time: 10.0, cost: 800 }, '洛阳': { time: 5.0, cost: 400 } } }
        };

        // 初始化地图
        function initMap() {
            const svg = document.getElementById('mapSvg');
            svg.innerHTML = '';

            // 绘制连接线
            Object.keys(cityData).forEach(city => {
                const cityInfo = cityData[city];
                Object.keys(cityInfo.connections).forEach(targetCity => {
                    const targetInfo = cityData[targetCity];
                    const connection = cityInfo.connections[targetCity];
                    
                    // 绘制连接线
                    const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
                    line.setAttribute('x1', cityInfo.x);
                    line.setAttribute('y1', cityInfo.y);
                    line.setAttribute('x2', targetInfo.x);
                    line.setAttribute('y2', targetInfo.y);
                    line.setAttribute('class', 'edge');
                    line.setAttribute('id', `edge-${city}-${targetCity}`);
                    svg.appendChild(line);

                    // 添加连接信息标签
                    const midX = (cityInfo.x + targetInfo.x) / 2;
                    const midY = (cityInfo.y + targetInfo.y) / 2;
                    
                    const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                    label.setAttribute('x', midX);
                    label.setAttribute('y', midY - 5);
                    label.setAttribute('class', 'edge-label');
                    label.textContent = `${connection.time}h/${connection.cost}¥`;
                    svg.appendChild(label);
                });
            });

            // 绘制城市节点
            Object.keys(cityData).forEach(city => {
                const cityInfo = cityData[city];
                
                // 城市圆圈
                const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
                circle.setAttribute('cx', cityInfo.x);
                circle.setAttribute('cy', cityInfo.y);
                circle.setAttribute('r', 15);
                circle.setAttribute('class', 'city-node');
                circle.setAttribute('id', `node-${city}`);
                svg.appendChild(circle);

                // 城市标签
                const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                text.setAttribute('x', cityInfo.x);
                text.setAttribute('y', cityInfo.y + 25);
                text.setAttribute('class', 'city-label');
                text.textContent = city;
                svg.appendChild(text);
            });
        }

        // Dijkstra算法实现
        function dijkstra(graph, start, end, weightFunc) {
            const distances = {};
            const previous = {};
            const visited = new Set();
            const queue = [];

            // 初始化距离
            Object.keys(graph).forEach(node => {
                distances[node] = node === start ? 0 : Infinity;
                queue.push({ node, distance: distances[node] });
            });

            while (queue.length > 0) {
                // 找到最小距离的节点
                queue.sort((a, b) => a.distance - b.distance);
                const current = queue.shift();
                
                if (visited.has(current.node) || current.distance === Infinity) continue;
                if (current.node === end) break;

                visited.add(current.node);

                // 更新邻居节点距离
                Object.keys(graph[current.node].connections).forEach(neighbor => {
                    if (!visited.has(neighbor)) {
                        const connection = graph[current.node].connections[neighbor];
                        const weight = weightFunc(connection);
                        const newDistance = distances[current.node] + weight;

                        if (newDistance < distances[neighbor]) {
                            distances[neighbor] = newDistance;
                            previous[neighbor] = current.node;
                            
                            // 更新队列中的距离
                            const neighborInQueue = queue.find(item => item.node === neighbor);
                            if (neighborInQueue) {
                                neighborInQueue.distance = newDistance;
                            }
                        }
                    }
                });
            }

            // 重构路径
            const path = [];
            let current = end;
            while (current !== undefined) {
                path.unshift(current);
                current = previous[current];
            }

            return { path, totalCost: distances[end] };
        }

        // A*算法实现
        function astar(graph, start, end, weightFunc) {
            const openSet = [{ node: start, g: 0, f: 0 }];
            const closedSet = new Set();
            const gScore = { [start]: 0 };
            const fScore = { [start]: heuristic(start, end) };
            const previous = {};

            while (openSet.length > 0) {
                openSet.sort((a, b) => a.f - b.f);
                const current = openSet.shift();

                if (current.node === end) {
                    const path = [];
                    let node = end;
                    while (node !== undefined) {
                        path.unshift(node);
                        node = previous[node];
                    }
                    return { path, totalCost: gScore[end] };
                }

                closedSet.add(current.node);

                Object.keys(graph[current.node].connections).forEach(neighbor => {
                    if (closedSet.has(neighbor)) return;

                    const connection = graph[current.node].connections[neighbor];
                    const tentativeG = gScore[current.node] + weightFunc(connection);

                    if (!gScore.hasOwnProperty(neighbor) || tentativeG < gScore[neighbor]) {
                        previous[neighbor] = current.node;
                        gScore[neighbor] = tentativeG;
                        fScore[neighbor] = gScore[neighbor] + heuristic(neighbor, end);

                        if (!openSet.find(item => item.node === neighbor)) {
                            openSet.push({ 
                                node: neighbor, 
                                g: gScore[neighbor], 
                                f: fScore[neighbor] 
                            });
                        }
                    }
                });
            }

            return { path: [], totalCost: Infinity };
        }

        // 启发式函数(欧几里得距离)
        function heuristic(city1, city2) {
            const pos1 = cityData[city1];
            const pos2 = cityData[city2];
            return Math.sqrt(Math.pow(pos1.x - pos2.x, 2) + Math.pow(pos1.y - pos2.y, 2)) / 100;
        }

        // 权重函数
        function getWeightFunction() {
            const timeWeight = parseFloat(document.getElementById('timeWeight').value);
            const costWeight = parseFloat(document.getElementById('costWeight').value);
            
            return (connection) => {
                return timeWeight * connection.time + costWeight * (connection.cost / 100);
            };
        }

        // 运行指定算法
        function runAlgorithm(algorithm) {
            const startCity = document.getElementById('startCity').value;
            const endCity = document.getElementById('endCity').value;
            
            if (startCity === endCity) {
                alert('起始城市和目标城市不能相同!');
                return;
            }

            const weightFunc = getWeightFunction();
            let result;

            switch (algorithm) {
                case 'dijkstra':
                    result = dijkstra(cityData, startCity, endCity, weightFunc);
                    break;
                case 'astar':
                    result = astar(cityData, startCity, endCity, weightFunc);
                    break;
                case 'weighted':
                    result = dijkstra(cityData, startCity, endCity, weightFunc);
                    break;
            }

            displayResult(result, algorithm);
            highlightPath(result.path);
        }

        // 算法对比
        function compareAlgorithms() {
            const startCity = document.getElementById('startCity').value;
            const endCity = document.getElementById('endCity').value;
            
            if (startCity === endCity) {
                alert('起始城市和目标城市不能相同!');
                return;
            }

            const timeWeightFunc = (conn) => conn.time;
            const costWeightFunc = (conn) => conn.cost / 100;
            const weightedFunc = getWeightFunction();

            const results = {
                '最短时间(Dijkstra)': dijkstra(cityData, startCity, endCity, timeWeightFunc),
                '最低费用(Dijkstra)': dijkstra(cityData, startCity, endCity, costWeightFunc),
                '加权优化(Dijkstra)': dijkstra(cityData, startCity, endCity, weightedFunc),
                'A*算法': astar(cityData, startCity, endCity, weightedFunc)
            };

            displayComparison(results);
        }

        // 显示单个结果
        function displayResult(result, algorithm) {
            if (result.path.length === 0) {
                document.getElementById('resultContent').innerHTML = '<p>无法找到路径!</p>';
                document.getElementById('results').style.display = 'block';
                return;
            }

            const totalTime = calculateTotalTime(result.path);
            const totalCost = calculateTotalCost(result.path);

            let html = `
                <div class="path-info">
                    <strong>算法:</strong> ${getAlgorithmName(algorithm)}<br>
                    <strong>路径:</strong> ${result.path.join(' → ')}<br>
                    <strong>总时间:</strong> ${totalTime.toFixed(1)} 小时<br>
                    <strong>总费用:</strong> ¥${totalCost}<br>
                    <strong>综合评分:</strong> ${result.totalCost.toFixed(2)}
                </div>
                <div class="path-steps">
                    <h5>详细路径:</h5>
            `;

            for (let i = 0; i < result.path.length - 1; i++) {
                const from = result.path[i];
                const to = result.path[i + 1];
                const connection = cityData[from].connections[to];
                html += `
                    <div class="step">
                        <span>${from}${to}</span>
                        <span>${connection.time}h / ¥${connection.cost}</span>
                    </div>
                `;
            }

            html += '</div>';
            document.getElementById('resultContent').innerHTML = html;
            document.getElementById('results').style.display = 'block';
        }

        // 显示算法对比结果
        function displayComparison(results) {
            let html = '<h5>算法性能对比:</h5>';
            html += `
                <table class="comparison-table">
                    <tr>
                        <th>算法</th>
                        <th>路径</th>
                        <th>总时间(h)</th>
                        <th>总费用(¥)</th>
                        <th>综合评分</th>
                    </tr>
            `;

            let bestTime = Infinity, bestCost = Infinity, bestScore = Infinity;
            const processedResults = {};

            // 计算各项指标
            Object.keys(results).forEach(algorithmName => {
                const result = results[algorithmName];
                if (result.path.length > 0) {
                    const totalTime = calculateTotalTime(result.path);
                    const totalCost = calculateTotalCost(result.path);
                    
                    processedResults[algorithmName] = {
                        ...result,
                        totalTime,
                        totalCost
                    };

                    bestTime = Math.min(bestTime, totalTime);
                    bestCost = Math.min(bestCost, totalCost);
                    bestScore = Math.min(bestScore, result.totalCost);
                }
            });

            // 生成表格行
            Object.keys(processedResults).forEach(algorithmName => {
                const data = processedResults[algorithmName];
                const isTimeWinner = data.totalTime === bestTime;
                const isCostWinner = data.totalCost === bestCost;
                const isScoreWinner = data.totalCost === bestScore;

                html += `
                    <tr>
                        <td>${algorithmName}</td>
                        <td>${data.path.join(' → ')}</td>
                        <td class="${isTimeWinner ? 'best-result' : ''}">${data.totalTime.toFixed(1)}</td>
                        <td class="${isCostWinner ? 'best-result' : ''}">${data.totalCost}</td>
                        <td class="${isScoreWinner ? 'best-result' : ''}">${data.totalCost.toFixed(2)}</td>
                    </tr>
                `;
            });

            html += '</table>';
            
            // 高亮最优路径
            const bestResult = Object.values(processedResults).find(r => r.totalCost === bestScore);
            if (bestResult) {
                highlightPath(bestResult.path);
            }

            document.getElementById('resultContent').innerHTML = html;
            document.getElementById('results').style.display = 'block';
        }

        // 高亮显示路径
        function highlightPath(path) {
            // 清除之前的高亮
            document.querySelectorAll('.path-edge').forEach(edge => {
                edge.classList.remove('path-edge');
            });

            // 高亮新路径
            for (let i = 0; i < path.length - 1; i++) {
                const from = path[i];
                const to = path[i + 1];
                const edge = document.getElementById(`edge-${from}-${to}`) || 
                           document.getElementById(`edge-${to}-${from}`);
                if (edge) {
                    edge.classList.add('path-edge');
                }
            }
        }

        // 辅助函数
        function calculateTotalTime(path) {
            let total = 0;
            for (let i = 0; i < path.length - 1; i++) {
                total += cityData[path[i]].connections[path[i + 1]].time;
            }
            return total;
        }

        function calculateTotalCost(path) {
            let total = 0;
            for (let i = 0; i < path.length - 1; i++) {
                total += cityData[path[i]].connections[path[i + 1]].cost;
            }
            return total;
        }

        function getAlgorithmName(algorithm) {
            const names = {
                'dijkstra': 'Dijkstra算法',
                'astar': 'A*算法',
                'weighted': '加权优化算法'
            };
            return names[algorithm] || algorithm;
        }

        // 权重滑块事件处理
        document.getElementById('timeWeight').addEventListener('input', function() {
            const value = this.value;
            document.getElementById('timeWeightValue').textContent = value;
            document.getElementById('costWeight').value = (1 - parseFloat(value)).toFixed(1);
            document.getElementById('costWeightValue').textContent = (1 - parseFloat(value)).toFixed(1);
        });

        document.getElementById('costWeight').addEventListener('input', function() {
            const value = this.value;
            document.getElementById('costWeightValue').textContent = value;
            document.getElementById('timeWeight').value = (1 - parseFloat(value)).toFixed(1);
            document.getElementById('timeWeightValue').textContent = (1 - parseFloat(value)).toFixed(1);
        });

        // 页面加载完成后初始化
        window.addEventListener('load', function() {
            initMap();
        });
    </script>
</body>
</html> 
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。