P1034 矩形覆盖【1】

本文探讨了一个平面点集的覆盖问题,使用k个矩形(k<=4)来覆盖n个点,目标是最小化覆盖面积之和。通过深度优先搜索(DFS)结合剪枝策略,实现了对覆盖方案的优化,确保了矩形之间的独立性,并提供了完整的C++代码实现。

题目描述

题目描述

在平面上有 n 个点(n <= 50),每个点用一对整数坐标表示。例如:当 n=4 时,4个点的坐标分另为:p1(1,1),p2(2,2),p3(3,6),P4(0,7),见图一。

这些点可以用 k 个矩形(1<=k<=4)全部覆盖,矩形的边平行于坐标轴。当 k=2 时,可用如图二的两个矩形 sl,s2 覆盖,s1,s2 面积和为 4。问题是当 n 个点坐标和 k 给出后,怎样才能使得覆盖所有点的 k 个矩形的面积之和为最小呢。约定:覆盖一个点的矩形面积为 0;覆盖平行于坐标轴直线上点的矩形面积也为0。各个矩形必须完全分开(边线与顶点也都不能重合)。
输入输出格式
输入格式:

n k xl y1 x2 y2 ... ...

xn yn (0<=xi,yi<=500)

输出格式:

输出至屏幕。格式为:

一个整数,即满足条件的最小的矩形面积之和。
 

 

输入输出样例

输入样例#1: 复制

4 2
1 1
2 2
3 6
0 7

输出样例#1: 复制

4

最最保险的做法:dfs+剪枝,当k>=4时也能做。

原则:枚举每一个点,可以任意加入到k个矩形中的某一个,从而得到最优解。

原理:易证明对于每一种i(i<k)个矩形的情形,其结果一定不是最优的(可将其中某个矩形再次划分)

每个矩形只需记录其4至点,即可求面积。

#include<cstdio>
#include<iostream>
using namespace std;
struct dot
{
    int x,y;
} a[51];
struct mtrx
{
    int l,r,u,d;
    bool flag;
} p[5];
int n,m,ans=0x7f7f7f7f;
bool in(mtrx a, int x, int y)
{
    if (x>=a.l&&x<=a.r&&y>=a.d&&y<=a.u) return 1;
    return 0;
}
bool judge(mtrx a, mtrx b)
{
    if (in(a,b.l,b.u)) return 1;
    if (in(a,b.l,b.d)) return 1;
    if (in(a,b.r,b.u)) return 1;
    if (in(a,b.r,b.d)) return 1;
    return 0;
}
void dfs(int num)
{
    int value=0;
    for (int i=1; i<=m; i++)
    {
      if (p[i].flag)
      {
        for (int j=i+1; j<=m; j++)
          if (p[i].flag&&judge(p[i],p[j])) return;
      }
      value+=(p[i].r-p[i].l)*(p[i].u-p[i].d);
    }
    if (value>=ans) return;
    if (num>n){
        ans=value;
        return;
    }
    for (int i=1; i<=m; i++)
    {
        mtrx tmp=p[i]; //tmp的初值 
        if (p[i].flag==0)
        {
            p[i].flag=1;
            p[i].l=p[i].r=a[num].x;
            p[i].u=p[i].d=a[num].y;
            dfs(num+1); p[i]=tmp;    //回溯 (一定要回溯彻底,用tmp变量) 
            break;
        }
        else 
        {
            p[i].r=max(p[i].r,a[num].x); p[i].l=min(p[i].l,a[num].x);
            p[i].u=max(p[i].u,a[num].y); p[i].d=min(p[i].d,a[num].y);
            dfs(num+1);
            p[i]=tmp;   //回溯 (一定要回溯彻底,用tmp变量) 
        }    
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
      scanf("%d%d",&a[i].x,&a[i].y);
    dfs(1);
    printf("%d",ans);
    return 0;
}

 

%% 粒子群算法求解最小生成树 function [best_mst, best_weight] = pso_mst(graph, pop_size, max_iter) % 输入: graph - 邻接矩阵 % pop_size - 粒子群大小 % max_iter - 最大迭代次数 % 输出: best_mst - 最佳MST列表 % best_weight - 最佳权重 n = size(graph, 1); % 节数量 num_edges = n - 1; % MST数量 % 初始化粒子群 particles = cell(pop_size, 1); velocities = cell(pop_size, 1); pbest = cell(pop_size, 1); pbest_weight = inf(1, pop_size); % 全局最佳 gbest_weight = inf; gbest = []; % 初始化粒子 for i = 1:pop_size % 随机生成树作为粒子 particles{i} = generate_random_spanning_tree(graph); velocities{i} = zeros(size(particles{i})); pbest{i} = particles{i}; pbest_weight(i) = tree_weight(graph, particles{i}); % 更新全局最佳 if pbest_weight(i) < gbest_weight gbest_weight = pbest_weight(i); gbest = particles{i}; end end % PSO参数 w = 0.7; % 惯性权重 c1 = 1.5; % 个体学习因子 c2 = 1.5; % 社会学习因子 % 迭代优化 for iter = 1:max_iter for i = 1:pop_size % 更新速度 r1 = rand(); r2 = rand(); velocities{i} = w * velocities{i} + ... c1 * r1 * (pbest{i} - particles{i}) + ... c2 * r2 * (gbest - particles{i}); % 更新位置 (离散化处理) new_tree = update_tree(particles{i}, velocities{i}, graph); % 计算适应度 weight = tree_weight(graph, new_tree); % 更新个体最佳 if weight < pbest_weight(i) pbest_weight(i) = weight; pbest{i} = new_tree; % 更新全局最佳 if weight < gbest_weight gbest_weight = weight; gbest = new_tree; end end particles{i} = new_tree; end % 动态调整惯性权重 w = w * 0.99; fprintf('迭代 %d: 最佳权重 = %.2f\n', iter, gbest_weight); end % 转换结果为列表 [best_mst, best_weight] = tree_to_edges(gbest, graph); end %% 辅助函数 function tree = generate_random_spanning_tree(graph) % 生成随机生成树 (Prim变体) n = size(graph, 1); visited = false(1, n); tree = zeros(n); % 邻接矩阵表示树 start = randi(n); visited(start) = true; nodes_in_tree = start; while sum(visited) < n candidates = []; for node = nodes_in_tree neighbors = find(graph(node, :) > 0 & ~visited); for nbr = neighbors candidates = [candidates; node, nbr, graph(node, nbr)]; end end if isempty(candidates) break; end % 随机选择一条 idx = randi(size(candidates, 1)); edge = candidates(idx, :); tree(edge(1), edge(2)) = edge(3); tree(edge(2), edge(1)) = edge(3); visited(edge(2)) = true; nodes_in_tree = [nodes_in_tree, edge(2)]; end end function weight = tree_weight(graph, tree) % 计算树的权重 weight = sum(sum(tree)) / 2; % 邻接矩阵对称,除以2避免重复计算 end function new_tree = update_tree(tree, velocity, graph) % 基于速度更新树结构 n = size(tree, 1); new_tree = tree; % 概率性添加/删除 for i = 1:n for j = i+1:n if velocity(i, j) > rand() % 添加 (如果存在且不在树中) if graph(i, j) > 0 && tree(i, j) == 0 % 检查是否形成环 temp_tree = new_tree; temp_tree(i, j) = graph(i, j); temp_tree(j, i) = graph(i, j); if ~has_cycle(temp_tree) new_tree = temp_tree; end end elseif velocity(i, j) < -rand() % 删除 (如果存在) if tree(i, j) > 0 % 检查是否保持连通 temp_tree = new_tree; temp_tree(i, j) = 0; temp_tree(j, i) = 0; if is_connected(temp_tree) new_tree = temp_tree; end end end end end end function [edges, weight] = tree_to_edges(tree, graph) % 将邻接矩阵转换为列表 n = size(tree, 1); edges = []; weight = 0; for i = 1:n for j = i+1:n if tree(i, j) > 0 edges = [edges; i, j, tree(i, j)]; weight = weight + tree(i, j); end end end end %% 连通性和环检测函数 function connected = is_connected(tree) % 检查图是否连通 (BFS) n = size(tree, 1); visited = false(1, n); queue = 1; % 从节1开始 while ~isempty(queue) node = queue(1); queue(1) = []; visited(node) = true; neighbors = find(tree(node, :) > 0); for nbr = neighbors if ~visited(nbr) queue = [queue, nbr]; end end end connected = all(visited); end function cycle = has_cycle(tree) % 检查图是否有环 (DFS) n = size(tree, 1); visited = false(1, n); for i = 1:n if ~visited(i) if dfs_cycle(tree, i, visited, -1) cycle = true; return; end end end cycle = false; end function found = dfs_cycle(tree, node, visited, parent) % DFS环检测辅助函数 visited(node) = true; neighbors = find(tree(node, :) > 0); for nbr = neighbors if ~visited(nbr) if dfs_cycle(tree, nbr, visited, node) found = true; return; end elseif nbr ~= parent found = true; return; end end found = false; end %% 测试PSO算法 [best_mst, best_weight] = pso_mst(graph, 20, 50); fprintf(‘\nPSO算法MST结果:\n’); disp(array2table(best_mst, ‘VariableNames’, {‘Node1’, ‘Node2’, ‘Weight’})); fprintf(‘总权重: %.2f\n’, best_weight); 请用以上的PSO算法替换下列洪水算法解决IEEE33节的孤岛划分问题,并对比总结优缺:%% IEEE33节孤岛划分程序(修复版) function island_division(varargin) % 输入参数: % index - 时段索引(1或2),可选 % visualize_flood - 是否可视化洪水算法扩散过程(true/false),可选 % 使用示例: % island_division() % 默认时段1,不可视化扩散 % island_division(1) % 时段1,不可视化扩散 % island_division(2, true) % 时段2,可视化扩散过程 % island_division(‘visualize’, true) % 时段1,可视化扩散 %% 步骤1:处理输入参数 visualize_flood = false; % 默认不可视化扩散过程 index = 1; % 默认时段1 for i = 1:length(varargin) if ischar(varargin{i}) && strcmpi(varargin{i}, ‘visualize’) visualize_flood = varargin{i+1}; elseif isnumeric(varargin{i}) && ismember(varargin{i}, [1, 2]) index = varargin{i}; end end fprintf(‘运行时段: %d, 可视化扩散: %d\n’, index, visualize_flood); %% 步骤2:加载完整IEEE33节数据 mpc = load_case_ieee33(); % 加载完整IEEE33数据 Nb = 33; % 节数 Nt = 24; % 时段数 %% 步骤3:系统参数设置 % 光储系统接入位置和出力 Y_DG = [6, 8, 12, 23, 30]; % 光储系统接入位置 P_PV = [425, 200, 480, 120, 600; % 时段1各光伏出力 700, 500, 720, 620, 720]; % 时段2各光伏出力 % 负荷等级和需求系数 wf = 0.01*ones(1,Nb); % 负荷等级系数 wf([6, 12, 23, 30]) = 1; % 一级负荷 wf([1,5,8,14,18,21,22,27,29,32]) = 0.1; % 二级负荷 Ct = zeros(Nb, Nt); % 负荷时变需求系数 Ct([6, 12, 23, 30], 😃 = 10; % 一级负荷的需求度恒为10 Ct(setdiff(1:Nb, [6, 12, 23, 30]), 😃 = ones(Nb-4,1)10… [7.7796,5.3248,5.0429,5.1984,2.9881,3.4293,7.2042,10.6853,… 12.5477,15.5212,24.8697,33.2302,24.2863,24.7989,36.3317,… 27.4948,17.8808,14.0970,18.9046,20.4888,22.2151,19.7628,21.4368,17.6810]/36.3317; Ft = Ct.*(wf’*ones(1, Nt)); % 优先恢复系数 % 负荷总量和节负荷 PL = [3305, 4870]; % 负荷总量 total_original_load = sum(mpc.bus(:,3)); % 原始总负荷3715 kW P_load = zeros(Nb, 2); for t = 1:2 P_load(:, t) = mpc.bus(:,3) * (PL(t) / total_original_load); end % 可控负荷设置 a_cut = zeros(1,Nb); % 削减系数 C_load1 = [3,13,20,24]; % 完全可控的负荷集合 a_cut(C_load1) = 1; % 完全可控 C_load2 = [2,7,10]; % 50%可控的负荷集合 a_cut(C_load2) = 0.5; % 50%可控 %% 步骤4:故障支路设置 fault_branch = [5, 25, 20]; % 故障支路 %% 步骤5:确定当前时段 time0 = [7, 15]; % 故障发生时刻 time_value = time0(index); % 当前时刻值 time_idx = index; % 当前时段索引 %% 步骤6:确定优先恢复集合 F_Rload = {[6, 12, 15, 23, 28, 30], [6, 12, 23, 30, 32]}; %% 步骤7:构建邻接矩阵(考虑故障支路断开) branch = mpc.branch; A = zeros(Nb); for i = 1:size(branch, 1) from = branch(i, 1); to = branch(i, 2); A(from, to) = 1; A(to, from) = 1; % 无向图 end % 断开故障支路 for i = 1:length(fault_branch) br = fault_branch(i); from = branch(br, 1); to = branch(br, 2); A(from, to) = 0; A(to, from) = 0; end %% 步骤8:使用改进洪水算法形成初始孤岛 visited_global = false(1, Nb); % 全局访问标记 island_node = cell(1, length(Y_DG)); % 各孤岛节集合 island_power = zeros(1, length(Y_DG)); % 各孤岛光伏出力 island_load = zeros(1, length(Y_DG)); % 各孤岛负荷总量 % 生成节标 node_coords = generate_radial_coords(Nb); % 修复:创建可视化窗口的句柄数组 fig_handles = gobjects(1, length(Y_DG)); % 预分配图形对象句柄 for k = 1:length(Y_DG) % 创建洪水算法可视化窗口 if visualize_flood fig_handles(k) = figure(‘Name’, sprintf(‘洪水算法扩散 - 光储节%d’, Y_DG(k)), … ‘Position’, [100, 100, 800, 600]); ax = axes(fig_handles(k)); visualize_graph(ax, A, node_coords, fault_branch); hold(ax, ‘on’); plot(ax, node_coords(Y_DG(k), 1), node_coords(Y_DG(k), 2), … ‘p’, ‘MarkerSize’, 20, ‘MarkerFaceColor’, ‘y’, ‘MarkerEdgeColor’, ‘k’); title(ax, sprintf(‘从节%d开始扩散 (功率: %.1f kW)’, Y_DG(k), P_PV(time_idx, k))); drawnow; else fig_handles(k) = gobjects(1); % 创建空图形对象 end % 修复:使用正确的参数调用FLA_power [island_node{k}, island_load(k)] = FLA_power(... Y_DG(k), ... % 光伏节 P_PV(time_idx, k), ...% 光伏出力 A, ... % 邻接矩阵 P_load, ... % 负荷数据 time_idx, ... % 当前时段索引 visited_global, ... % 全局访问标记 visualize_flood, ... % 可视化标志 node_coords, ... % 节标 fig_handles(k)); % 可视化窗口句柄 % 更新全局访问状态 visited_global(island_node{k}) = true; island_power(k) = P_PV(time_idx, k); % 关闭可视化窗口 if visualize_flood pause(1); % 显示1秒后关闭 close(fig_handles(k)); end end %% 步骤9:处理孤岛交叉(融合重叠孤岛) island_node_cross = {}; % 融合后的孤岛 island_cross_DG = {}; % 融合后的孤岛对应的光伏节 island_power_cross = []; % 融合后的孤岛总出力 island_load_cross = []; % 融合后的孤岛总负荷 % 初始化:第一个孤岛 if ~isempty(island_node{1}) island_node_cross{1} = island_node{1}; island_cross_DG{1} = 1; island_power_cross(1) = island_power(1); island_load_cross(1) = island_load(1); end % 处理后续孤岛 for k = 2:length(Y_DG) merged = false; % 检查是否与已有孤岛重叠 for i = 1:length(island_node_cross) if ~isempty(intersect(island_node{k}, island_node_cross{i})) % 合并孤岛 island_node_cross{i} = union(island_node_cross{i}, island_node{k}); island_cross_DG{i} = union(island_cross_DG{i}, k); island_power_cross(i) = island_power_cross(i) + island_power(k); island_load_cross(i) = island_load_cross(i) + island_load(k); merged = true; break; end end % 若无重叠,创建新孤岛 if ~merged && ~isempty(island_node{k}) island_node_cross{end+1} = island_node{k}; island_cross_DG{end+1} = k; island_power_cross(end+1) = island_power(k); island_load_cross(end+1) = island_load(k); end end %% 步骤10:连接优先恢复负荷 island_node_all = []; for i = 1:length(island_node_cross) island_node_all = union(island_node_all, island_node_cross{i}); end for k = 1:length(F_Rload{index}) node = F_Rload{index}(k); if ~ismember(node, island_node_all) % 查找最近孤岛 min_dist = inf; target_island = 0; for i = 1:length(island_node_cross) % 使用洪水算法计算距离 dist = FLA_distance(node, island_node_cross{i}, A); if dist < min_dist min_dist = dist; target_island = i; end end % 将节加入目标孤岛 if target_island > 0 island_node_cross{target_island} = union(... island_node_cross{target_island}, node); island_node_all = union(island_node_all, node); % 更新孤岛负荷 island_load_cross(target_island) = island_load_cross(target_island) + P_load(node, time_idx); end end end %% 步骤11:负荷削减(满足功率平衡) for i = 1:length(island_node_cross) if island_power_cross(i) < island_load_cross(i) % 按节优先级排序(优先切除低优先级负荷) [~, idx] = sort(Ft(island_node_cross{i}, time_idx), ‘ascend’); sorted_nodes = island_node_cross{i}(idx); % 逐步切除负荷直到满足功率平衡 for j = 1:length(sorted_nodes) node = sorted_nodes(j); if ismember(node, C_load1) % 完全可控负荷 island_load_cross(i) = island_load_cross(i) - P_load(node, time_idx); island_node_cross{i} = setdiff(island_node_cross{i}, node); elseif ismember(node, C_load2) % 50%可控负荷 island_load_cross(i) = island_load_cross(i) - 0.5*P_load(node, time_idx); % 更新节剩余负荷 P_load(node, time_idx) = 0.5*P_load(node, time_idx); end % 检查是否满足功率平衡 if island_power_cross(i) >= island_load_cross(i) break; end end % 如果仍不满足,继续切除其他负荷 if island_power_cross(i) < island_load_cross(i) [~, idx] = sort(Ft(island_node_cross{i}, time_idx), 'ascend'); sorted_nodes = island_node_cross{i}(idx); for j = 1:length(sorted_nodes) node = sorted_nodes(j); island_load_cross(i) = island_load_cross(i) - P_load(node, time_idx); island_node_cross{i} = setdiff(island_node_cross{i}, node); if island_power_cross(i) >= island_load_cross(i) break; end end end end end %% 步骤12:输出孤岛划分方案 fprintf(‘\n===== 时段%d (%d:00) 孤岛划分结果 =====\n’, index, time_value); fprintf(‘故障支路: %s\n’, mat2str(fault_branch)); fprintf(‘优先恢复负荷: %s\n\n’, mat2str(F_Rload{index})); for i = 1:length(island_node_cross) fprintf(‘孤岛%d:\n’, i); fprintf(’ - 光储系统: %s (总功率: %.1f kW)\n’, … mat2str(Y_DG(island_cross_DG{i})), … island_power_cross(i)); fprintf(’ - 包含节: %s\n’, mat2str(sort(island_node_cross{i}))); fprintf(’ - 负荷总量: %.1f kW\n’, island_load_cross(i)); fprintf(’ - 功率平衡: %.1f kW (利用率: %.1f%%)\n’, … island_power_cross(i) - island_load_cross(i), … 100*island_load_cross(i)/island_power_cross(i)); fprintf(‘----------------------------------------\n’); end %% 可视化孤岛划分结果 visualize_islands(mpc, island_node_cross, Y_DG, fault_branch, index); end %% 改进洪水算法核心函数(修复版) function [island_nodes, total_load] = FLA_power(start_node, max_power, A, P_load, idx_time, visited_global, visualize, node_coords, fig_handle) % FLA洪水算法改进版 - 含可视化功能 queue = []; island_nodes = []; total_load = 0; diffusion_speed = 0.2; % 扩散速度(秒/步) % 1. 处理起始节 if ~visited_global(start_node) visited_global(start_node) = true; queue = [queue, start_node]; island_nodes = [island_nodes, start_node]; total_load = total_load + P_load(start_node, idx_time); % 可视化起始节(添加安全检查和条件判断) if visualize && isvalid(fig_handle) ax = fig_handle.CurrentAxes; plot(ax, node_coords(start_node, 1), node_coords(start_node, 2), ... 'o', 'MarkerSize', 12, 'MarkerFaceColor', 'g', 'MarkerEdgeColor', 'k'); text(ax, node_coords(start_node, 1)+0.2, node_coords(start_node, 2)+0.2, ... sprintf('%.1f kW', P_load(start_node, idx_time)), 'FontSize', 9); drawnow; pause(diffusion_speed); end else return; % 起始节已访问,直接返回 end % 2. 洪水算法扩散过程 step = 0; while ~isempty(queue) step = step + 1; current = queue(1); queue(1) = []; % 可视化当前节(添加安全检查和条件判断) if visualize && isvalid(fig_handle) ax = fig_handle.CurrentAxes; plot(ax, node_coords(current, 1), node_coords(current, 2), ... 'o', 'MarkerSize', 10, 'MarkerFaceColor', 'b', 'MarkerEdgeColor', 'k'); drawnow; pause(diffusion_speed); end % 获取当前节的所有邻居 all_neighbors = find(A(current, :)); % 检查每个邻居的可行性和功率约束 for neighbor = all_neighbors % 跳过已访问节 if visited_global(neighbor) continue; end % 计算添加该节后的新负荷 new_load = total_load + P_load(neighbor, idx_time); % 功率约束检查 if new_load <= max_power % 标记为已访问 visited_global(neighbor) = true; % 加入队列和孤岛 queue = [queue, neighbor]; island_nodes = [island_nodes, neighbor]; total_load = new_load; % 更新总负荷 % 可视化新加入节(添加安全检查和条件判断) if visualize && isvalid(fig_handle) ax = fig_handle.CurrentAxes; plot(ax, node_coords(neighbor, 1), node_coords(neighbor, 2), ... 'o', 'MarkerSize', 8, 'MarkerFaceColor', 'r', 'MarkerEdgeColor', 'k'); text(ax, node_coords(neighbor, 1)+0.2, node_coords(neighbor, 2)+0.2, ... sprintf('%.1f kW', P_load(neighbor, idx_time)), 'FontSize', 8); % 绘制连接线 line(ax, [node_coords(current, 1), node_coords(neighbor, 1)], ... [node_coords(current, 2), node_coords(neighbor, 2)], ... 'Color', 'g', 'LineWidth', 2, 'LineStyle', '-'); % 更新标题 title(ax, sprintf('从节%d扩散 (已加入%d节, 负荷:%.1f/%.1f kW)', ... start_node, length(island_nodes), total_load, max_power)); drawnow; pause(diffusion_speed/2); end end end end % 可视化最终孤岛(添加安全检查和条件判断) if visualize && isvalid(fig_handle) && length(island_nodes) >= 3 ax = fig_handle.CurrentAxes; % 绘制孤岛界 k = convhull(node_coords(island_nodes, 1), node_coords(island_nodes, 2)); plot(ax, node_coords(island_nodes(k), 1), node_coords(island_nodes(k), 2), ... 'Color', 'm', 'LineWidth', 2, 'LineStyle', '--'); % 添加功率平衡信息 text(ax, 0.5, -1.5, sprintf('最终孤岛: %d节, 负荷: %.1f kW (利用率: %.1f%%)', ... length(island_nodes), total_load, 100*total_load/max_power), ... 'FontSize', 10, 'BackgroundColor', 'w'); drawnow; end end %% 洪水算法距离计算函数 function [dist] = FLA_distance(start, targets, A) % 洪水算法计算最短距离 n = size(A, 1); visited = false(1, n); distance = inf(1, n); queue = []; visited(start) = true; distance(start) = 0; queue = [queue, start]; while ~isempty(queue) current = queue(1); queue(1) = []; % 检查是否到达目标 if ismember(current, targets) dist = distance(current); return; end % 扩散到邻居节 neighbors = find(A(current, :) & ~visited); for neighbor = neighbors visited(neighbor) = true; distance(neighbor) = distance(current) + 1; queue = [queue, neighbor]; end end dist = inf; % 未找到路径 end %% 网络可视化辅助函数 function visualize_graph(ax, A, node_coords, fault_branch) % 绘制网络基础结构 hold(ax, ‘on’); grid(ax, ‘on’); axis(ax, ‘equal’); % 绘制节 n = size(A, 1); for i = 1:n plot(ax, node_coords(i, 1), node_coords(i, 2), ... 'ko', 'MarkerSize', 8, 'MarkerFaceColor', 'w'); text(ax, node_coords(i, 1)+0.1, node_coords(i, 2)+0.1, ... num2str(i), 'FontSize', 9); end % 绘制支路 [rows, cols] = find(triu(A)); % 只绘制上三角避免重复 for i = 1:length(rows) from = rows(i); to = cols(i); x = [node_coords(from, 1), node_coords(to, 1)]; y = [node_coords(from, 2), node_coords(to, 2)]; % 检查是否为故障支路 is_fault = false; for j = 1:length(fault_branch) br = fault_branch(j); if (from == br && to == br) || ... % 自环 (from == br || to == br) % 连接故障节 is_fault = true; break; end end if is_fault plot(ax, x, y, 'r--', 'LineWidth', 1.5); else plot(ax, x, y, 'k-', 'LineWidth', 1); end end title(ax, 'IEEE33节配电网拓扑'); xlabel(ax, 'X标'); ylabel(ax, 'Y标'); end %% 修复版孤岛可视化函数 function visualize_islands(mpc, islands, dg_nodes, fault_branch, index) % 可视化孤岛划分结果 - 修复凸包计算问题 fig = figure(‘Name’, ['时段 ‘, num2str(index), ’ 孤岛划分’], … ‘Position’, [100, 100, 900, 700]); ax = axes(fig); hold(ax, ‘on’); % 生成节标 node_coords = generate_radial_coords(33); % 绘制基础网络 visualize_graph(ax, mpc2adjacency(mpc), node_coords, fault_branch); % 绘制孤岛 colors = lines(length(islands)); legend_handles = []; legend_labels = {}; for i = 1:length(islands) nodes = islands{i}; if isempty(nodes) continue; end % === 修复凸包计算问题 === % 检查节是否共线 is_collinear = check_collinear(node_coords(nodes, :)); % 绘制孤岛界(修复凸包问题) if length(nodes) >= 3 && ~is_collinear % 正常情况:计算凸包 k = convhull(node_coords(nodes, 1), node_coords(nodes, 2)); plot(ax, node_coords(nodes(k), 1), node_coords(nodes(k), 2), ... 'Color', colors(i, :), 'LineWidth', 2, 'LineStyle', '--'); elseif length(nodes) >= 2 % 节共线或只有2个节:绘制最小包围矩形 [rect_x, rect_y] = min_bounding_rect(node_coords(nodes, :)); plot(ax, rect_x, rect_y, ... 'Color', colors(i, :), 'LineWidth', 2, 'LineStyle', '--'); end % ====================== % 绘制孤岛节 for j = 1:length(nodes) h = plot(ax, node_coords(nodes(j), 1), node_coords(nodes(j), 2), ... 'o', 'MarkerSize', 10, 'MarkerFaceColor', colors(i, :), ... 'MarkerEdgeColor', 'k'); % 添加节负荷标签 text(ax, node_coords(nodes(j), 1)+0.15, node_coords(nodes(j), 2)-0.15, ... sprintf('%.1f', mpc.bus(nodes(j), 3)), 'FontSize', 8); if j == 1 legend_handles(end+1) = h; legend_labels{end+1} = ['孤岛 ', num2str(i)]; end end end % 标记光储系统 for i = 1:length(dg_nodes) h = plot(ax, node_coords(dg_nodes(i), 1), node_coords(dg_nodes(i), 2), ... 'p', 'MarkerSize', 15, 'MarkerFaceColor', 'y', 'MarkerEdgeColor', 'k'); if i == 1 legend_handles(end+1) = h; legend_labels{end+1} = '光储系统'; end end % 标记故障支路 h = plot(ax, NaN, NaN, 'r--', 'LineWidth', 1.5); legend_handles(end+1) = h; legend_labels{end+1} = '故障支路'; % 添加孤岛信息标注 info_str = sprintf('时段 %d 孤岛划分结果', index); text(ax, min(node_coords(:,1))-1, max(node_coords(:,2))+1, info_str, ... 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', 'w'); % 标题和标签 title(ax, ['IEEE33节孤岛划分 - 时段 ', num2str(index)]); % 图例 legend(ax, legend_handles, legend_labels, 'Location', 'Best'); hold(ax, 'off'); end %% 检查集是否共线 function is_collinear = check_collinear(points) % 检查集是否共线(位于同一直线上) if size(points, 1) < 3 is_collinear = true; return; end % 计算向量叉积 v1 = points(2,:) - points(1,:); for i = 3:size(points, 1) v2 = points(i,:) - points(1,:); cross_product = v1(1)*v2(2) - v1(2)*v2(1); % 2D叉积 if abs(cross_product) > 1e-5 is_collinear = false; return; end end is_collinear = true; end %% 计算最小包围矩形 function [rect_x, rect_y] = min_bounding_rect(points) % 计算集的最小包围矩形 if size(points, 1) == 1 % 单情况 rect_x = [points(1,1)-0.5, points(1,1)+0.5, points(1,1)+0.5, points(1,1)-0.5, points(1,1)-0.5]; rect_y = [points(1,2)-0.5, points(1,2)-0.5, points(1,2)+0.5, points(1,2)+0.5, points(1,2)-0.5]; return; end % 计算凸包(如果可能) try k = convhull(points(:,1), points(:,2)); hull_points = points(k,:); catch % 如果凸包计算失败(共线),使用所有 hull_points = points; end % 计算最小包围矩形 min_x = min(hull_points(:,1)); max_x = max(hull_points(:,1)); min_y = min(hull_points(:,2)); max_y = max(hull_points(:,2)); % 创建矩形标 rect_x = [min_x, max_x, max_x, min_x, min_x]; rect_y = [min_y, min_y, max_y, max_y, min_y]; end %% 从mpc生成邻接矩阵 function A = mpc2adjacency(mpc) branch = mpc.branch; n = size(mpc.bus, 1); A = zeros(n); for i = 1:size(branch, 1) from = branch(i, 1); to = branch(i, 2); A(from, to) = 1; A(to, from) = 1; % 无向图 end end %% 完整IEEE33节系统数据加载 function mpc = load_case_ieee33() % IEEE33节系统完整数据 mpc.version = ‘2’; mpc.baseMVA = 10; % 节数据 [节编号 类型 有功负荷(kW) 无功负荷(kvar)] mpc.bus = [ 1 3 0.00 0.00; 2 1 100.00 60.00; 3 1 90.00 40.00; 4 1 120.00 80.00; 5 1 60.00 30.00; 6 1 60.00 20.00; 7 1 200.00 100.00; 8 1 200.00 100.00; 9 1 60.00 20.00; 10 1 60.00 20.00; 11 1 45.00 30.00; 12 1 60.00 35.00; 13 1 60.00 35.00; 14 1 120.00 80.00; 15 1 60.00 10.00; 16 1 60.00 20.00; 17 1 60.00 20.00; 18 1 90.00 40.00; 19 1 90.00 40.00; 20 1 90.00 40.00; 21 1 90.00 40.00; 22 1 90.00 40.00; 23 1 90.00 50.00; 24 1 420.00 200.00; 25 1 420.00 200.00; 26 1 60.00 25.00; 27 1 60.00 25.00; 28 1 60.00 20.00; 29 1 120.00 70.00; 30 1 200.00 600.00; 31 1 150.00 70.00; 32 1 210.00 100.00; 33 1 60.00 40.00; ]; % 支路数据 [起始节 终止节 电阻(Ω) 电抗(Ω)] mpc.branch = [ 1 2 0.0922 0.0470; 2 3 0.4930 0.2511; 3 4 0.3660 0.1864; 4 5 0.3811 0.1941; 5 6 0.8190 0.7070; 6 7 0.1872 0.6188; 7 8 0.7114 0.2351; 8 9 1.0300 0.7400; 9 10 1.0440 0.7400; 10 11 0.1966 0.0650; 11 12 0.3744 0.1238; 12 13 1.4680 1.1550; 13 14 0.5416 0.7129; 14 15 0.5910 0.5260; 15 16 0.7463 0.5450; 16 17 1.2890 1.7210; 17 18 0.7320 0.5740; 2 19 0.1640 0.1565; 19 20 1.5042 1.3554; 20 21 0.4095 0.4784; 21 22 0.7089 0.9373; 3 23 0.4512 0.3083; 23 24 0.8980 0.7091; 24 25 0.8960 0.7011; 6 26 0.2030 0.1034; 26 27 0.2842 0.1447; 27 28 1.0590 0.9337; 28 29 0.8042 0.7006; 29 30 0.5075 0.2585; 30 31 0.9744 0.9630; 31 32 0.3105 0.3619; 32 33 0.3410 0.5302; ]; end %% 生成放射状拓扑标 function coords = generate_radial_coords(n) % 生成放射状拓扑标 coords = zeros(n, 2); % 主馈线节1-18) for i = 1:18 coords(i, :) = [i-1, 0]; end % 分支119-22) coords(19, :) = [1, -1]; coords(20, :) = [2, -1]; coords(21, :) = [3, -1]; coords(22, :) = [4, -1]; % 分支2(23-25) coords(23, :) = [2, 1]; coords(24, :) = [3, 1]; coords(25, :) = [4, 1]; % 分支3(26-33) coords(26, :) = [5, 1]; coords(27, :) = [6, 1]; coords(28, :) = [7, 1]; coords(29, :) = [8, 1]; coords(30, :) = [9, 1]; coords(31, :) = [10, 1]; coords(32, :) = [11, 1]; coords(33, :) = [12, 1]; end
最新发布
08-19
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值