(四)四色地图
子渊和爸爸都喜欢旅游,每次在选择旅游地点之前,他们都要在爸爸书房里的中国地图面前看上半天,以决定出游路线。
又一次,子渊惊奇地发现,虽然地图上色彩斑斓,可是仔细数起来颜色却不超过4种。真是一个“伟大”的发现啊!子渊连忙把他的发现告诉了爸爸,谁知道爸爸一点也不惊讶,只是笑眯眯地点开了一个网页“四色原理”:
世界近代三大数学难题之一(另外两个是费马定理和哥德巴赫猜想)。四色猜想的提出来自英国。1852年,毕业于伦敦大学的弗南西斯•格思里(Francis Guthrie)来到一家科研单位搞地图着色工作时,发现了一种有趣的现象:“看来,每幅地图都可以用四种颜色着色,使得有共同边界的国家着上不同的颜色。”,用数学语言表示,即“将平面任意地细分为不相重迭的区域,每一个区域总可以用1,2,3,4这四个数字之一来标记,而不会使相邻的两个区域得到相同的数字。”这个结论能不能从数学上加以严格证明呢?他和在大学读书的弟弟格里斯决心试一试。兄弟二人为证明这一问题而使用的稿纸已经堆了一大叠,可是研究工作没有进展。
1852年10月23日,他的弟弟就这个问题的证明请教他的老师、著名数学家德•摩尔根,摩尔根也没有能找到解决这个问题的途径,于是写信向自己的好友、著名数学家哈密尔顿爵士请教。哈密尔顿接到摩尔根的信后,对四色问题进行论证。但直到1865年哈密尔顿逝世为止,问题也没有能够解决。
1872年,英国当时最著名的数学家凯利正式向伦敦数学学会提出了这个问题,于是四色猜想成了世界数学界关注的问题。世界上许多一流的数学家都纷纷参加了四色猜想的大会战。1878~1880年两年间,著名的律师兼数学家肯普和泰勒两人分别提交了证明四色猜想的论文,宣布证明了四色定理,大家都认为四色猜想从此也就解决了。
11年后,即1890年,数学家赫伍德以自己的精确计算指出肯普的证明是错误的。不久,泰勒的证明也被人们否定了。后来,越来越多的数学家虽然对此绞尽脑汁,但一无所获。于是,人们开始认识到,这个貌似容易的题目,其实是一个可与费马猜想相媲美的难题:先辈数学大师们的努力,为后世的数学家揭示四色猜想之谜铺平了道路。
进入20世纪以来,科学家们对四色猜想的证明基本上是按照肯普的想法在进行。1913年,伯克霍夫在肯普的基础上引进了一些新技巧,美国数学家富兰克林于1939年证明了22国以下的地图都可以用四色着色。1950年,有人从22国推进到35国。1960年,有人又证明了39国以下的地图可以只用四种颜色着色;随后又推进到了50国。看来这种推进仍然十分缓慢。电子计算机问世以后,由于演算速度迅速提高,加之人机对话的出现,大大加快了对四色猜想证明的进程。1976年,在J. Koch的算法的支持下,美国数学家阿佩尔(Kenneth Appel)与哈肯(Wolfgang Haken)在美国伊利诺斯大学的两台不同的电子计算机上,用了1200个小时,作了100亿判断,终于完成了四色定理的证明。四色猜想的计算机证明,轰动了世界,当时中国科学家也有在研究这原理。它不仅解决了一个历时100多年的难题,而且有可能成为数学史上一系列新思维的起点。
下面我们也用计算机来验证一下四色问题:
设有下列形状的图形:有n个区域(0<n <101),各区域的相邻关系用0(不相邻),1(相邻)表示。
将上面图形的每一部分涂上红(1),黄(2),蓝(3),绿(4)四种颜色之一,要求相邻部分的颜色不同。请输出一种涂色方案。
输入数据:第一行为一个整数n(0<n <101);第2至n+1行为一个n*n矩阵(元素值为0或1)。
输出数据:各个区域的颜色码,如:
1->1(表示区域1的颜色为红色)
2->3(表示区域2的颜色为蓝色)
3->2
4->4
四色猜想的计算机证明!真是一个艰巨而伟大的工作啊!就让我们展开思维的翅膀,在算法的空间自由翱翔吧!
确定涂色方案的方法我们称为试错法(也有人称探索法,专业术语是回溯法)。
我们首先将区域1涂颜色1(即红色),并将颜色1入栈,准备涂区域2,从颜色1开始试探。
区域i能否涂颜色j,关键取决于区域i的周边是否有同色区域。搜索已涂色的区域1~i-1中与区域i相邻的区域k,看看区域k是否涂了颜色j。
如果所有的区域k都没有涂颜色j,则说明区域i可以涂颜色j,我们将颜色j入栈,准备涂下一个区域i+1,并从颜色1开始试探。
如果区域k中有涂颜色j,那区域i就不能涂颜色j,于是我们继续试探颜色j+1;如果j已经是最后一种颜色(在本题中j=4时表示j是最后一种颜色),我们就应该将上一区域(i-1)的颜色出栈,重新为区域(i-1)选择涂色方案,当然颜色要从stack[i-1]+1开始。
就这样,从区域1出发,依此类推,直至区域n涂好颜色为止。
代码如下:
{代码9:}
{四色猜想的计算机证明}
PROGRAM FourColor(input, output);
CONST
MAXREGION = 100;
MAXCOLOR = 4; {最多4种颜色}
VAR
lib : array[1..MAXREGION, 1..MAXREGION] of 0..1;
color : array[1..MAXREGION] of 0..MAXCOLOR;
flag : BOOLEAN;
n, i, j, k : integer;
{判断区域k的周边是否有同色区域}
FUNCTION RightColor(k : integer) : BOOLEAN;
var
i : integer;
begin
for i:=1 to k-1 do
if (lib[k, i] = 1) and (color[k] = color[i]) then
begin
RightColor := false;
exit;
end; {if}
RightColor := true;
end; {RightColor}
BEGIN
writeln('Input n:');
readln(n);
Fillchar(color, sizeof(color), 0); {颜色代码初始化为0,加1后从1开始}
randomize;
for i:=1 to n do {随机设置各区域的关系}
for j:=1 to i do
begin
lib[i, j] := random(2);
lib[j, i] := lib[i, j];
end; {for}
for i:=1 to n do {设置对角线为0}
lib[i, i] := 0;
for i:=1 to n do
begin
for j:=1 to n do
write(lib[i, j]:2);
writeln;
end; {for}
k := 1;
flag := true;
while (k >= 1) and flag do
begin
inc(color[k]); {当前颜色加1}
while (color[k] <= MAXCOLOR) and (not RightColor(k)) do {当前颜色是否有效}
inc(color[k]); {无效继续试探下一颜色}
if color[k] <= MAXCOLOR then {颜色有效}
begin
if k = n then {所有区域填色成功}
flag := false
else {非最后的区域,继续试探下一区域}
inc(k);
end {if}
else {颜色无效且已经是最后一种颜色,回溯到前一个区域}
begin
color[k] := 0;
dec(k);
end; {else}
end; {while}
for i:=1 to n do
writeln(i, ' -> ', color[i]);
END.
看完子渊的代码,爸爸露出了欣慰的笑容:“子渊啊,代码写得还可以,看来对于栈的应用你已经入门了,栈是我们构造算法的一个重要工具,今后用到栈的地方还很多,比如在回溯思想中的应用等等。
前面我们学习了栈的特点和基本操作,并进行了简单地运用。希望你要深入理解栈的基本思想,在实际算法中熟练运用这种工具,写出更加简练和清晰的代码。
接下来我给你准备了一些配套的练习,希望你认真地思考,构造精妙算法,写出漂亮的代码。