多路口交通燈問題

乀-penguin發表於2024-09-09

1.問題背景

如下圖,有A,B,C,D,E五個路口,其中A,D路口為單行道,方向均指向道中。現考慮修交通燈,其功能如下:

  • 交通燈上有若干顏色不同的燈

  • 同一時刻有且只有一種顏色的燈亮

  • 每個路口都會安裝一個交通燈,用於指示交通。在相同的路口,不同燈亮代表不同的通行情況。

現需要設計交通燈,使得在正常指示交通的前提下,交通燈上顏色數量儘量少。

注意:

  • 按實際交通狀況,一條道路上右側為正行,左側為逆行。
  • 路口右轉不會與其他通行情況產生較大沖突(正常情況),因此右轉不必設燈。

2.問題分析

交通問題,先從十字路口考慮。

十字路口,每個路口的交通燈上都有3種顏色的交通燈,其中黃燈只做提醒或觀察作用,因此真正決定交通狀態的是紅燈與綠燈。綠燈亮則通行,紅燈亮則停止。但相同的道路,其燈展示情況也可不同,例如:

設A,B路口直通,C,D路口直通,其形成了一個十字路口。則道路通行情況可以為

  1. A\(\rightarrow\)B,B\(\rightarrow\)A為初始通行情況,隨後A,B各自停止直行,共同左轉。再停止左轉,轉為C\(\rightarrow\)D,D\(\rightarrow\)C,然後類似繼續下去。

  2. A\(\rightarrow\)B,B\(\rightarrow\)A為初始通行情況,隨後B道路停止通行,A左轉、直行共同進行。再A停止直行,B啟動左轉,其餘與1類似。

可以發現,以上情況所需最少燈顏色數為2,即只需紅燈與綠燈即可實現上述交通情況,但是情況不唯一。

可以預料到的一點是,隨著路口數的增加,情況必然更加多樣與複雜,即使想設計從任意道路進,再存在順序時刻(即特定亮燈時刻)使得可以從任意非單行道(這裡指進入道路中心的單行道,即從其他道路無法進入)出,則更加不容易。當然,隨著交通指示燈數量的增加,必然更容易實現上述,但是也會造成更多的時間冗雜,極有可能導致交通擁擠,這也不是我們需要的。

實際上多於4條交通道路的複雜交通,更適合設計成環島。本題是跳出常見交通情況,探索一般交通狀況。

迴歸本題,右轉由於不會與其餘交通情況衝突,因此不作考慮,故需要考慮的則為道路衝突情況。下圖給出了兩種衝突情況

並且,該交通上可能存在許多衝突情況,關於具體的衝突情況,可以採用列舉的方式,固定一條道路,列舉其餘道路,再判斷是否衝突。就本題而言較為容易,但一旦道路情況複雜,則需要根據實際要求(例如進單行,出單行)進行合適的約束。

假設我們已經得到了交通道路衝突情況,接下來就可以進行建模了。本質上,這是一道圖論的基本問題。

3.建立數學模型

最初的想法是把道路盡頭視作點,這樣中心路口的轉向即可看作邊。但是交通燈處理的問題本質上是使道路正常通行,不會發生衝突。正如上述所說,應該以從某路口轉向某路口視作基本操作物件,即點。而點與點相連可以視作道路衝突,那麼就是說當一方某色燈亮起時,另一方的某色燈一定不能亮。無妨設這兩種燈顏色一樣(實際上不一樣也不影響本質區別,只是某路口交通燈內部順序交換。當然不同顏色交通燈通向哪個路口應在交通告示牌上有所體現),則這就變成了圖論中最基本的問題——圖最小染色問題。

圖最小染色問題是指,一個圖中同一邊的兩點顏色不同,問最少需要多少種顏色數可以將所有點染色。這個基本問題對應了一個經典演算法:Welsh–Powell 演算法。該演算法是說,有一種貪心的方式可以儘量優的求出較小顏色數,根據OI Wiki上的說法,是在不限制最大顏色數的前提下,該演算法成立。可以證明的是,該貪心演算法是正確的,並且時間複雜度為\(O(n^2)\)

Welsh–Powell 演算法的過程是,對點按度數從高到低排序,然後從度數最高的點開始,依次染色。一個要求是:每次迴圈對應染上一種顏色,且保證同色不相連。這樣所染出的顏色數就是較小的著色數。先說時間複雜度,按顏色可以將點分組,每組內部需要判斷是否與其他點相連,對於第k組顏色內的點集即為\(O(n^2_k)\)的時間複雜度,但是在同一個顏色中列舉點時額外產生的時間複雜度為\(O(n)\)。由於\(\sum n_k=n\),並且列舉點最多n次,所以總複雜度為 \(\sum O(n^2_k)+O(n^2)\) ,即\(O(n^2)\)級。而直接列舉考慮顏色數則是np問題,時間複雜度非多項式級。故可見Welsh-Powell演算法極大的方便了對圖最小染色數的計算,對解決生活中實際問題也很有幫助。但是值得注意的一點是,該演算法得到的顏色數可能並非最優解,這與起始節點和圖形性質皆有關係。但在圖形較簡單及節點度數不高的情況下,該演算法效能優良。並且可以保證最大顏色數不會超過最大節點度數加一。具體證明參見 \(OI\) \(Wiki\),大體思路是按照演算法思路將每種顏色所染的節點歸為一類,再根據數學歸納法遞推得到類的性質。

這樣就解決了這個問題,下面是程式碼及執行結果。

4.實際執行

程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
	int num;
	int dis;
};
node t[100];
int edge[100][100],colorn=0,n;
int col[100],cnt,pre[100],rt;
bool cmp1(node p,node q) { return p.dis>q.dis; }
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		t[i].num=i;
		for(int j=1;j<=n;j++)
		{
			int v;
			scanf("%d",&v);
			if(v)
			{
				edge[i][j]=v;
				t[i].dis++;
				t[j].dis++;	
			}
		}
	}
	sort(t+1,t+n+1,cmp1);
	while(colorn<n)
	{
		rt=1;
		for(int i=1;i<=n;i++)
		{
			if(!col[t[i].num])
			{
				col[t[i].num]=++cnt;
				pre[rt]=i;
				colorn++;
				break;
			}
		}
		for(int i=1;i<=n;i++)
		{
			if(col[t[i].num]) continue;
			int flag=0;
			for(int j=1;j<=rt;j++)
			{
				if(edge[t[pre[j]].num][t[i].num]) 
				{
					flag=1;
					break;
				}
			}
			if(!flag) 
			{
				col[t[i].num]=cnt;
				colorn++;
				pre[++rt]=i;
			}
		}
	}
	printf("min-color-number:%d\n",cnt);
	for(int i=1;i<=n;i++) printf("num%d's color is %d\n",i,col[i]);
	return 0;
}

下面是執行結果:

min-color-number:3
num1's color is 2
num2's color is 1
num3's color is 1
num4's color is 3
num5's color is 2
num6's color is 1
num7's color is 3
num8's color is 3
num9's color is 2

當然,這只是理論上最小顏色數。比如B-E段實際只需兩個交通燈。但以次觀之,十字路口交通燈只需兩色:分別管理直行和左轉,但這樣顯然是不好的。故設計為綠燈通行紅燈停止。上述也可如此,對於不同通行方向,皆用綠燈但用不同指向表示,這樣在加上紅燈限定停止的同時可以更方便車輛通行。以上問題實際上與此方式相同。

5.總結

該問題帶來了一個不同的問題思考方式,即生活中常見的是十字路口、丁字路口等,但五個通道的路口也是可能存在的。這樣如何最最佳化設計交通燈實際上是一個問題。在建模後,這個問題就與圖論中的一個基本問題重合了。然後透過經典演算法,就可以初步得出結論。但是正如上文所說,還應切換度數相同的節點再次實驗,在所有備選中選出最符合道路實情的一種交通通行設計方式。

總而言之,將實際生活與演算法相結合,藉助計算機的強大算力,可以更方便的處理一些事情。程式碼當然很經典也很簡單了,算是剛回歸的一個小練手吧。

相關文章