2019美赛D题算法之一~
通过设定危险度规则,使用基于双端队列的01bfs算法快速计算每个格子的危险度,使元胞自发地倾向危险度更低的方向来模拟人员疏散模型。
注:代码已修改。修改后的代码和输入数据见https://download.csdn.net/download/m0_37809890/10945394
% 元胞自动机
clc; clear;
map = map_read();
colormap([0.8 0.8 0.8;1 1 1;1 0 0;0 1 1;0 1 1]);
danger = cal_danger(map);
danger_order = cal_danger_oreder(danger);
map = add_people(map, 0.20);
[K,t] = move(map, danger, danger_order);
function [K,t] = move(map, danger, danger_order)
% 移动
K = zeros(4000,3); t = 0;
map_show(map);
sel = zeros(1,27);
while(size(find(map==1),1))
t=t+1;
K(t,1)=size(find(map(30:110, 340:360 )==1),1);
K(t,2)=size(find(map(3:25, 210:280 )==1),1);
K(t,3)=size(find(map(110:162,420:460 )==1),1);
go = [0,-1; 0,1; -1,0; 1,0; 1,1; 1,-1; -1,1; -1,-1; 0,0];
% flag = 0;
for i = 1:size(danger_order,1)
ua = danger_order(i,1); ub = danger_order(i,2);
if map(ua,ub)~=1
continue;
end
mx = 0; cnt = 0;
for j = 1:size(go,1)
va = ua+go(j,1); vb = ub+go(j,2);
if(map_at(map,va,vb)>1)
mx = max(mx,danger(va,vb));
end
end
for j = 1:size(go,1)
va = ua+go(j,1); vb = ub+go(j,2);
if(map_at(map,va,vb)>1)
for k = 1:mx+1-danger(va,vb)
cnt = cnt+1; sel(cnt)=j;
end
end
end
if(cnt==0)
continue;
end
target = sel(unidrnd(cnt));
va = ua+go(target,1); vb = ub+go(target,2);
map(ua,ub)=2;
if(map(va,vb)==2)
map(va,vb)=1;
end
end
map_show(map);
pause(0.01);
end
end
function res = map_at(map,a,b)
% 如果(a,b)在map内,返回map(a,b),否则返回0
if(a>0&&b>0&&a<=size(map,1)&&b<=size(map,2))
res = map(a,b);
else
res = 0;
end
end
function map = map_read()
% 读入地图
load("./floor_1/map2_2.mat",'A');
map = A';
% map = [
% 0, 0, 0, 0, 0, 0
% 0, 2, 2, 1, 2, 0
% 3, 2, 0, 2, 1, 0
% 0, 2, 2, 2, 1, 0
% 0, 0, 0, 0, 0, 0
% ];
end
function map = add_people(map,rate)
% 向地图里面加人
rnd = rand(size(map));
for i = 1:size(map,1)
for j = 1:size(map,2)
if rnd(i,j)=0)
flag = 0;
for k=size(go,1)
va=ua+go(k,1); vb=ub+go(k,2);
if(map_at(map,va,vb)==0)
flag=1;
break;
end
end
if(flag)
danger(ua,ub)=danger(ua,ub)+0;
end
end
end
end
end
function danger = bfs_01(map,danger,st)
% 01bfs计算单种出口影响的复杂度
n = numel(find(map~=0));
lef=n; rig=n; que = zeros(n*2,2); % 初始化双端队列
que(rig,:)=st; rig = rig+1; %push_back
danger(st(1),st(2)) = 0;
while(lef=1)
nd = danger(ua,ub)+2;
if(danger(ua,ub)==0 && map(ua,ub)==map(va,vb))
nd = 0;
end
if(danger(va,vb)==-1 || danger(va,vb)>nd)
danger(va,vb) = nd;
if(nd==0)
lef=lef-1; que(lef,:)=[va,vb];%push_front
else
que(rig, :)=[va,vb]; rig=rig+1; % push_back
end
end
end
end
end
end
function danger_order = cal_danger_oreder(danger)
% 将危险度进行排序
n = numel(find(danger~=-1));
danger_order = zeros(n,3); k = 1;
for i = 1:size(danger,1)
for j = 1:size(danger,2)
if(danger(i,j)~=-1)
danger_order(k,1) = i;
danger_order(k,2) = j;
danger_order(k,3) = danger(i,j);
k = k + 1;
end
end
end
[~, pos] = sort(danger_order(:,3));
danger_order = danger_order(pos,1:2);
end