问题描述
三名商人各带–个随从乘船渡河,一只小船只能容纳二人,由他们自己划行.随从们密约,在河的任一岸,一旦随从的人数比商人多,就杀人越货.但是如何乘船渡河的大权掌握在商人们手中.商人们怎样才能安全渡河呢?
对于这类智力游戏,经过一番逻辑思索是可以找出解决办法的.这里用数学模型求解,一是为了给出建模的示例,二是因为这类模型可以解决相当广泛的一类问题,比逻辑思索的结果容易推广.
问题分析
问题已经理想化,所以可以抽象成一个多步决策问题,即在往返的过程中都要保证两岸及船上的商人数大于等于随从数。用允许安全状态变量表示岸上的人员状态,允许决策变量表示船上的人员状况,可以找到状态随决策变化的规律,问题转换为在允许状态下的决策达到安全渡河。
建立模型
此处自行参考《数学模型》姜启源第四版的——商人们怎样安全过河的案例
以下给出代码实现的流程图
代码
main.m
%% 商人安全过河问题
close, clear, clc
%% 声明全局变量
% setOfAllowedStates 允许状态集合
% setOfDecisionVariables 允许决策集合
% collectionOfStatusMarkers 状态标记集合
% numberOfBusinessmen 商人的总人数
% numberOfFollowers 随从的总人数
% maximumLoadCapacity 船的最大承载人数
% totalSetOfDecisionVariables 允许决策个数
% totalSetOfAllowedStates 允许状态个数
% 0ollectionOfStatusMarkersBegin 初始编号
% collectionOfStatusMarkersProcess 中间编号
% collectionOfStatusMarkersEnd 终止编号
global setOfAllowedStates setOfDecisionVariables collectionOfStatusMarkers numberOfBusinessmen collectionOfStatusMarkersProcess cnt k;
global collectionOfStatusMarkersBegin collectionOfStatusMarkersEnd numberOfFollowers maximumLoadCapacity totalSetOfDecisionVariables totalSetOfAllowedStates;
setOfAllowedStates = [];
setOfDecisionVariables = [];
totalSetOfDecisionVariables = 0;
totalSetOfAllowedStates = 0;
%% 输入
fprintf('输入大于3 3 2时候将不再作图\n')
numberOfBusinessmen = input('请输入商人的人数:');
numberOfFollowers = input('请输入随从的人数:');
maximumLoadCapacity = input('请输入船的最大承载人数:');
%% 作图属性
axis([0 numberOfBusinessmen 0 numberOfBusinessmen]);
set(gca, 'XTick', 0 : 1 : numberOfBusinessmen), set(gca, 'YTick', 0 : 1 : numberOfFollowers);
xlabel('此岸商人数'), ylabel('此岸随从数'), title('允许状态集合')
grid on
%% 允许状态集合
for businessmen = 0 : numberOfBusinessmen
for followers = 0 : numberOfFollowers
if(businessmen == 0 || businessmen == numberOfBusinessmen || (businessmen >= followers && numberOfBusinessmen - businessmen >= numberOfFollowers - followers))
totalSetOfAllowedStates = totalSetOfAllowedStates + 1;
setOfAllowedStates(totalSetOfAllowedStates, :) = [businessmen, followers];
hold on
plot(businessmen, followers, 'b*', 'MarkerSize', 10);
end
end
end
setOfAllowedStates = [setOfAllowedStates, ones(totalSetOfAllowedStates, 1); setOfAllowedStates, zeros(totalSetOfAllowedStates, 1)];
totalSetOfAllowedStates = totalSetOfAllowedStates * 2;
fprintf('共有%d种允许状态\n', totalSetOfAllowedStates)
printfAllowedStates = input('是否打印允许状态集合y/n?','s');
if printfAllowedStates == 'y'
fprintf('允许状态集合:')
setOfAllowedStates
end
%% 允许决策集合
for businessmen = 0 : numberOfBusinessmen
for followers = 0 : numberOfFollowers
if(businessmen + followers >= 1 && businessmen + followers <= maximumLoadCapacity)
totalSetOfDecisionVariables = totalSetOfDecisionVariables + 1;
setOfDecisionVariables(totalSetOfDecisionVariables, :) = [businessmen, followers];
end
end
end
fprintf('共%d个允许决策\n',totalSetOfDecisionVariables);
printfDecisionVariables = input('是否打印允许决策集合y/n?','s');
if printfDecisionVariables == 'y'
fprintf('允许决策集合:');
setOfDecisionVariables
end
%% 状态标记集合
% 初始状态利用哈希表
collectionOfStatusMarkers = zeros(totalSetOfAllowedStates, 1);
collectionOfStatusMarkersBegin = find(ismember(setOfAllowedStates, [numberOfBusinessmen, numberOfBusinessmen, 1], 'rows') == 1); % 初始状态编号
collectionOfStatusMarkersEnd = find(ismember(setOfAllowedStates, [0, 0, 0], 'rows') == 1); % 终止状态编号
cnt = 0;
k = 1;
collectionOfStatusMarkers(collectionOfStatusMarkersBegin) = 1;
collectionOfStatusMarkersProcess(1) = collectionOfStatusMarkersBegin;
%% 模拟过河(递归实现)
crossTheRiver(collectionOfStatusMarkersBegin);
if cnt == 0
fprintf('This question currently has no answer.\n')
else
fprintf('共有%d个方法\n', cnt)
end
crossTheRiver.m
function crossTheRiver(present)
global setOfAllowedStates setOfDecisionVariables collectionOfStatusMarkers collectionOfStatusMarkersProcess k numberOfBusinessmen;
global collectionOfStatusMarkersEnd totalSetOfDecisionVariables cnt;
if present == collectionOfStatusMarkersEnd
printCurrentResults();
if numberOfBusinessmen <= 3
plotCurrentResults(cnt + 1);
end
else
for i = 1 : totalSetOfDecisionVariables
possible(1, [1 2]) = setOfAllowedStates(present, [1 2]) + ((-1)^(setOfAllowedStates(present, 3))) * setOfDecisionVariables(i, :);
possible(1, 3) = 1 - setOfAllowedStates(present, 3);
tag = ismember(setOfAllowedStates, possible, 'rows');
if sum(tag) == 1
next = find(tag == 1);
if collectionOfStatusMarkers(next) == 0
collectionOfStatusMarkers(next) = 1;
k = k + 1;
collectionOfStatusMarkersProcess(k) = next;
crossTheRiver(next);
collectionOfStatusMarkers(next) = 0;
k = k - 1;
end
end
end
end
end
printCurrentResults.m
function printCurrentResults()
global setOfAllowedStates numberOfBusinessmen collectionOfStatusMarkersProcess cnt k numberOfFollowers
cnt = cnt + 1;
fprintf('方法 %d:\n',cnt);
fprintf('\t\t\t\t此\t岸\t\t\t\t\n');
fprintf('\t商人数\t\t\t随从数\t\t\t船的位置\n');
for i=1:k
fprintf('%d:\t %d\t\t\t\t %d',i,setOfAllowedStates(collectionOfStatusMarkersProcess(i),1),setOfAllowedStates(collectionOfStatusMarkersProcess(i),2));
if setOfAllowedStates(collectionOfStatusMarkersProcess(i),3)==1 % 船在此岸
fprintf('\t\t\t\t\t此岸\n');
else
fprintf('\t\t\t\t\t彼岸\n');
end
end
fprintf('\n');
end
plotCurrentResults.m
function plotCurrentResults(cntPicture)
global setOfAllowedStates collectionOfStatusMarkersProcess k numberOfBusinessmen numberOfFollowers;
figure(cntPicture)
axis([0 numberOfBusinessmen 0 numberOfBusinessmen]);
set(gca, 'XTick', 0 : 1 : numberOfBusinessmen), set(gca, 'YTick', 0 : 1 : numberOfFollowers);
xlabel('此岸商人数'), ylabel('此岸随从数'), title(['方法', num2str(cntPicture)])
grid on
for i = 2 : k
hold on
plot([setOfAllowedStates(collectionOfStatusMarkersProcess(i - 1),1), setOfAllowedStates(collectionOfStatusMarkersProcess(i),1)],...
[setOfAllowedStates(collectionOfStatusMarkersProcess(i - 1),2), setOfAllowedStates(collectionOfStatusMarkersProcess(i),2)], 'LineWidth',3)
pause(0.2)
end
end
动态展示
如有错误以及可以改进的地方欢迎在下方评论区留言!