Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
商人过河问题
三名商人各带一个随从乘船渡河。现此岸有一小船只能容纳两人,由他们自己划行。若在河的任一岸随从人数比商人多,他们就可能抢劫财物。不过如何乘船渡河的大权由商人们掌握。商人们怎样才能安全过河呢?
如何建模呢?
注意到,我们可以把商人、随从已经河的位置抽象为一个状态。随着小船载人过河,状态也就随着发生了转移。那么问题就简化为了,有一系列状态,并且状态之间可以转移,那么能够从最开始的状态转移至最后的状态呢?
状态的定义
**如何定义状态?**注意到我们只需要得知商人和随从分别在左岸的数量,那么右岸的人数可以直接推断出来。另外在表示一下船的位置(在左岸还是右岸),我们即可准确唯一的表达一个状态。
定义如下三元组:
其中 是商人在左岸的数量, 是随从在左岸的数量, 是船的位置(为0在左岸,为1在右岸)。
从而此三元组就能表示唯一的一个状态。
此时问题转化为:
有没有可行的解?
有些状态是不可行的!
很遗憾,并不是所有、 的任意 m 和 f 都可以构成一个可行的状态。不然,就能很轻易的把所有人都运到对岸。
哪些状态是可行的?从题意可知,只有两岸的商人数都大于等于随从数,或者商人数为0,才是可行的状态。可以用3维数组able[m][f][b]表示该状态是否可行。具体的算法如下:
for (int m = 0; m <= mNum; m++)
{
for (int f = 0; f <= fNum; f++)
{
int m1 = mNum - m, f1 = fNum - f;
//任何一边,商人比随从多或者商人为0为可行状态
if ((m >= f || m == 0) && (m1 >= f1 || m1 == 0))
{
able[m][f][0] = able[m][f][1] = 1;
printf("(%d %d %d) ", m, f, 0);
printf("(%d %d %d) \n", m, f, 1);
stateNum += 2;
}
}
}
如何状态转移?
我们虽然知道,状态的转移其实就是用船从将人运来运去。我们可以手写出所有状态转移的可能性,在这里列出一种:
但是既然我们要用计算机来解决问题,当然就不用自己手动枚举啦~~ 以下是我枚举状态转移的方式(纯纯暴力解法:
for (int d1 = 0; d1 <= bCap; d1++)
{
for (int d2 = 0; d2 <= bCap; d2++)
{
if (d1 + d2 <= bCap && d1 + d2 > 0)
{
int m1, f1, b1;
if (b == 1)
{
m1 = m + d1, f1 = f + d2, b1 = 0;
if (m1 <= mNum && f1 <= fNum && able[m1][f1][b1] && !found[m1][f1][b1])
//此时说明(m,f,b)可以转移到(m1,f1,b1)
}
else
{
m1 = m - d1, f1 = f - d2, b1 = 1;
if (m1 >= 0 && f1 >= 0 && able[m1][f1][b1] && !found[m1][f1][b1])
//此时说明(m,f,b)可以转移到(m1,f1,b1)
}
}
}
}
其中 和 是表示船运向对岸几个商人和随从。
那么,知道了有哪些状态,和如何转移状态,如何求是否有解?
图论又来大显身手了~
显然我们可以把状态(m,f,t)抽象为结点,把状态的转移抽象为图的边,即如果状态(m,f,t)可以转移到状态(m1,f1,t1),那么就把图中的从前者向后者连一条边即可~~~
最后,我们应用DFS深度搜索算法从起点(3,3,0)开始搜索,如果搜索到结点(0,0,1),就说明有方式,如果没有就说明无解啦~~~
int DFS(int m, int f, int b)
{
found[m][f][b] = 1;
if (m == 0 && f == 0 && b == 1)
{
found[m][f][b] = 0;
return 1;
}
int flag = 0;
for (int d1 = 0; d1 <= bCap; d1++)
{
for (int d2 = 0; d2 <= bCap; d2++)
{
if (d1 + d2 <= bCap && d1 + d2 > 0)
{
int m1, f1, b1;
if (b == 1)
{
m1 = m + d1, f1 = f + d2, b1 = 0;
if (!(m1 <= mNum && f1 <= fNum && able[m1][f1][b1] && !found[m1][f1][b1]))
continue;
}
else
{
m1 = m - d1, f1 = f - d2, b1 = 1;
if (!(m1 >= 0 && f1 >= 0 && able[m1][f1][b1] && !found[m1][f1][b1]))
continue;
}
if (DFS(m1, f1, b1))
return 1;
}
}
}
return 0;
}
答案
最后的答案显然是无解啦~~~