Tour 有向环覆盖。km最优匹配

本文介绍了一种利用KM匹配算法解决最小费用流问题的方法,通过详细的代码解析展示了如何求解有向图中的最小权值匹配问题。文章重点讨论了算法实现细节及常见错误,并给出了完整的C++代码实现。

这道题以前网络流的时候做过。最小费用流—有向环覆盖现在用km最优匹配,km最优匹配用来求最大权值,现在用来求最小权值。

易错点:边与边之间不要忘记初始化,-inf。当有重复边存在时保存权值小的。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
int n,m;
int w[505][505];
int slack[505];
int lx[505],ly[505];
int girl[1005];
bool visx[1005],visy[1005];
int dfs(int x)
{
    visx[x]=1;
    for(int i=1; i<=n; i++)
    {
        if(!visy[i])
        {
            int t=lx[x]+ly[i]-w[x][i];
            if(t==0) //判断该边是否被加入二分子图中
            {
                visy[i]=1;
                if(girl[i]==-1||dfs(girl[i]))
                {
                    girl[i]=x;
                    return 1;
                }
            }
            else if(slack[i]>t) //更新y中未被加入的最大边
            {
                slack[i]=t;
            }
        }
    }
    return 0;
}
int km()
{
    memset(girl,-1,sizeof(girl));
    memset(ly,0,sizeof(ly));
    for (int i = 1; i <= n; i ++)
    {
        lx[i]=-inf;
        for (int j = 1; j <= n; j ++) //初始化最大边
            if (w[i][j] > lx[i])
                lx[i] = w[i][j];
    }
    for(int x=1; x<=n; x++)
    {
        for(int j=1; j<=n; j++)
            slack[j]=inf;
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(x))
            {
                break;
            }
            //这个操作其实就是加边
            //方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,
            //所有在增广轨中的Y方点的标号全部加上一个常数d
            int d = inf;
            for (int i = 1; i <= n; i ++)
                if (!visy[i]&&d > slack[i])
                    d = slack[i];
            for(int i=1; i<=n; i++)
            {
                if(visx[i]) lx[i]-=d;
                if(visy[i]) ly[i]+=d;
                else
                    slack[i]-=d;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(girl[i]>-1)
            ans+=w[girl[i]][i];
    }
    return ans;
}
int main()
{
    int x,y;
    int a;
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                w[i][j]=-inf;
        }
        while(m--)
        {
            cin>>x>>y>>a;
            w[x][y]=max(w[x][y],-a);
        }
        printf("%d\n",-km());
    }
    return 0;
}

import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib import cm import os # ==================== 遗传算法参数配置 ==================== POP_SIZE = 200 # 种群规模 MAX_GEN = 500 # 最大迭代次数 CX_RATE = 0.85 # 交叉概率 MUT_RATE = 0.15 # 变异概率 ELITE_RATE = 0.1 # 精英保留比例 # ==================== 数据准备与预处理 ==================== def load_data(file_path): """加载并预处理数据""" df = pd.read_excel(file_path, skiprows=1, usecols="A:D") df.columns = ['节点', 'x', 'y', '需求'] df['需求'] = pd.to_numeric(df['需求'], errors='coerce').fillna(0) coordinates = df[['x', 'y']].values demands = df['需求'].values return coordinates, demands # ==================== 核心算法实现 ==================== class GeneticVRP: def __init__(self, coords, demands, capacity, pop_size): self.coords = coords self.demands = demands self.capacity = capacity self.pop_size = pop_size self.n = len(coords) self.dist_matrix = self.calc_distance_matrix() def calc_distance_matrix(self): """计算距离矩阵""" dist = np.zeros((self.n, self.n)) for i in range(self.n): for j in range(self.n): dist[i][j] = np.linalg.norm(self.coords[i] - self.coords[j]) return np.round(dist, 2) def init_population(self): """生成初始种群""" population = [] for _ in range(self.pop_size): # 随机生成满足载重约束的路径 unvisited = list(range(1, self.n)) routes = [] while unvisited: route = [0] # 起点为处理厂 load = 0 while unvisited and (load + self.demands[unvisited[0]] <= self.capacity): next_node = np.random.choice(unvisited) if load + self.demands[next_node] > self.capacity: break route.append(next_node) load += self.demands[next_node] unvisited.remove(next_node) route.append(0) # 返回处理厂 routes.append(route) population.append(routes) return population def fitness(self, individual): """计算适应度(总距离的倒数)""" total_dist = 0 for route in individual: for i in range(len(route)-1): total_dist += self.dist_matrix[route[i]][route[i+1]] return 1 / (total_dist + 1e-6) # 防止除零错误 def selection(self, population, fitnesses): """锦标赛选择""" selected = [] tournament_size = 5 for _ in range(len(population)): candidates = np.random.choice(len(population), tournament_size, replace=False) best_idx = candidates[np.argmax([fitnesses[c] for c in candidates])] selected.append(population[best_idx]) return selected def cx_partially_matched(self, ind1, ind2): """部分匹配交叉""" # 实现交叉操作,保留有效路径结构 # ...(具体实现代码较长,此处省略) def mut_inversion(self, individual): """路径倒置变异""" # 随机选择一段路径进行倒置 # ...(具体实现代码较长,此处省略) def evolve(self, population): """进化一代""" # 计算适应度 fitnesses = [self.fitness(ind) for ind in population] # 精英保留 elite_size = int(self.pop_size * ELITE_RATE) elite = sorted(zip(population, fitnesses), key=lambda x: x[1], reverse=True)[:elite_size] elite = [e[0] for e in elite] # 选择 selected = self.selection(population, fitnesses) # 交叉 offspring = [] for i in range(0, len(selected), 2): if np.random.rand() < CX_RATE: child1, child2 = self.cx_partially_matched(selected[i], selected[i+1]) offspring.extend([child1, child2]) # 变异 for i in range(len(offspring)): if np.random.rand() < MUT_RATE: offspring[i] = self.mut_inversion(offspring[i]) # 生成新种群 new_pop = elite + offspring while len(new_pop) < self.pop_size: new_pop.append(random.choice(population)) return new_pop[:self.pop_size] # ==================== 结果可视化 ==================== def plot_solution(routes, coords): plt.figure(figsize=(12, 10)) colors = cm.get_cmap('tab20', len(routes)) # 绘制所有节点 plt.scatter(coords[1:,0], coords[1:,1], c='black', s=50, label='收集点') plt.scatter(coords[0,0], coords[0,1], c='red', marker='*', s=300, label='处理厂') # 绘制路径 for i, route in enumerate(routes): path = coords[route] plt.plot(path[:,0], path[:,1], lw=2, color=colors(i), label=f'车辆{i+1} ({len(route)-2}个点)') plt.arrow(path[-2,0], path[-2,1], path[-1,0]-path[-2,0], path[-1,1]-path[-2,1], color=colors(i), head_width=0.5, length_includes_head=True) plt.title('优化后的垃圾运输路径', fontsize=14) plt.xlabel('X 坐标 (km)', fontsize=12) plt.ylabel('Y 坐标 (km)', fontsize=12) plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') plt.grid(alpha=0.3) plt.tight_layout() plt.show() # ==================== 主程序执行 ==================== if __name__ == "__main__": # 数据加载 file_path = "E:\sxjm\附件1(1).xlsx" coordinates, demands = load_data(file_path) capacity = 5.0 # 初始化算法 ga = GeneticVRP(coordinates, demands, capacity, POP_SIZE) population = ga.init_population() # 进化过程 best_fitness = [] for gen in range(MAX_GEN): population = ga.evolve(population) current_best = max([ga.fitness(ind) for ind in population]) best_fitness.append(1/current_best - 1e-6) print(f"Generation {gen+1}/{MAX_GEN} | 当前最优距离: {best_fitness[-1]:.2f} km") # 提取最优解 best_idx = np.argmin([sum(ga.calc_total_distance(ind)) for ind in population]) best_solution = population[best_idx] # 结果展示 total_dist = sum(ga.calc_total_distance(best_solution)) print(f"\n优化结果:") print(f"总使用车辆数:{len(best_solution)}") print(f"总行驶距离:{total_dist:.2f} km") # 可视化 plot_solution(best_solution, coordinates)
05-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值