演算法 電路佈線問題 (動態規劃)

📢 本文由 gemini-3-flash-preview 翻譯

問題描述

在一塊電路板的上下兩端分別有 n 個接線柱。根據電路設計,用 $(i, \pi(i))$ 表示將上端接線柱 i 與下端接線柱 $\pi(i)$ 相連,稱其為該電路板上的第 i 條連線

下圖所示的 $\pi(i)$ 排列為 $\{8, 7, 4, 2, 5, 1, 9, 3, 10, 6\}$。對於任何 $1 \le i < j \le n $ ,第 i 條連線和第 j 條連線相交的充要條件是 $\pi(i)>\pi(j)$

電路佈線示意

在製作電路板時,要求將這 n 條連線分佈到若干絕緣層上,在同一層上的連線不相交,現在要確定將哪些連線安排在一層上,使得該層上有盡可能多的連線,即確定連線集 $Nets=\{ (i,\pi(i)),1\le i\le n \}$ 的最大不相交子集

問題分析

記 $N(i,j)=\{ t\|(t,\pi(t))\in Nets, t\le i, \pi(t) \le j \}$ 。$N(i,j)$ 的最大不相交子集為 $MNS(i,j)$ ,$size(i,j)=\|MNS(i,j)\|$

經分析,該問題具有最佳子結構性質。對於規模為 n 的電路佈線問題,可以建構如下遞迴式

$$ \begin{align*} &(1) \ 當 \ i=1\ 時, size(1,j)= \begin{cases} 0, & \text{j<$\pi$(1)} \\ 1, & \text{其他情況} \end{cases} \\ &(2) \ 當 \ i>1\ 時, size(i,j)= \begin{cases} size(i-1,j), & \text{j<$\pi$(i)} \\ max{size(i-1,j),size(i-1,\pi(i)-1)+1}, & \text{其他情況} \end{cases} \end{align*} $$

C 程式碼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <stdio.h>
#include <stdlib.h>

#define N 10    // 問題規模

// 求最大不相交連接數
void maxNum(int pi[], int **size);

// 建構最大不相交連接集合,net[i]表示最大不相交子集中第i條連線的上端接線柱的序號
int constructSet(int pi[], int **size, int *net);

int main(void){
    // 索引從1開始
    int pi[N+1] = {0, 8, 7, 4, 2, 5, 1, 9, 3, 10, 6};
    int net[N];

    int **size;
    size = (int**)malloc(sizeof(int*)*(N+1));
    for(int i=0;i<N+1;i++)
        size[i]=(int*)malloc(sizeof(int)*(N+1));
    maxNum(pi, size);
    int m = constructSet(pi, size, net);   
    printf("最大不相交連接數為:%d\n",m);
    printf("包含的連線為:\n");
    for(int i=0; i<m; i++){
        printf("(%d,%d)\n", net[i], pi[net[i]]);
    }

}

void maxNum(int pi[], int **size){
    // size[i][j]: 上下端分別有i個和j個接線柱的電路板的第一層最大不相交連接數
    int i,j;

    // when j<pi(1)
    for(j=0; j<pi[1]; j++)
        size[1][j];

    // when j>=pi(1)
    for(j=pi[1]; j<=N; j++)
        size[1][j];

    for(i=2; i<N; i++){

        // when j<pi(i)
        for(j=0; j<pi[i]; j++)
            size[i][j] = size[i-1][j];   

        // when j>=c[i]
        for(j=pi[i]; j<=N; j++)
            size[i][j]=size[i-1][j]>=size[i-1][pi[i]-1]+1 ? size[i-1][j] : size[i-1][pi[i]-1]+1;
    }

    // 最大連接數
    size[N][N] = size[N-1][N]>=size[N-1][pi[N]-1]+1 ? size[N-1][N] : size[N-1][pi[N]-1]+1;
}

// 建構最大不相交連接集合,net[i]表示最大不相交子集中第i條連線的上端接線柱的序號
int constructSet(int pi[], int **size, int *net){
    int i;
    int j=N;
    int m=0;    // 記錄最大連接集合中的接線柱

    for(i=N; i>1; i--){ // 遞減
        // (i,pi[i])是最大不相交子集的一條連線
        if(size[i][j] != size[i-1][j]){
            net[m++]=i; // 將i記錄到陣列net中,連線數自增1
            j=pi[i]-1;  // 更新擴充連線柱區間
        }
    }

    // when i=1
    if(j>=pi[1])
        net[m++] = 1;

    return m;
}

其他

在搜尋過程中發現已有的文章: 算法设计与分析——电路布线(动态规划)

參考文章

LaTeX公式手册

Typora中使用LaTeX:多行公式左对齐

用malloc动态申请一个二维数组的三种方法