一、問題描述
在一塊電路板的上下兩端分別有n個接線柱。根據電路設計,要求用導線 (i,π(i)),將上端接線柱 i 與下端接線柱 π(i) 相連,
如圖,其中 π(i),1<=i<=n,是(1,2……,n)的一個排列。導線(i,π(i))稱為該電路板上的第i條連線。對於任何 1<=i<s<=n,第i條連線和第s條連線相交的充分且必要條件是 π(i) > π(s)。
在制作電路板時,要求將這n條線分布到若干個絕緣層上,在同一層上的連線不能相交。電路布線問題要確定將哪些連線安排在第一層上,使得該層上有盡可能多的連線。換句話說,該問題要求確定導線集Nets = { (i,π(i)),1<=i<=n }的最大不相交子集。
二、算法思路
1、最優子結構性質
N(i,j)表示上面節點i與下面節點j連線的左側區域內(包括i j連線)的連線集合,MNS(i,j)表示連線左側區域內最大不相交連線子集,Size(i,j)表示MNS(i,j)集合中連線的個數。
在這里注意N(i,j)和MNS(i,j)表示的都是集合!!內存儲的是連線,Size(i,j)存儲的才是最大不相交連線的個數!!
2、遞歸計算最優值
當i=1的時候很好理解。
當i>1時,我們還是看上面的那個連線圖。
當j<π(i)時,t[8][9]=t[7][9]。
當j>=π(i)時,t[7][9]=[6][8]+1。琢磨一下是不是符合表達式?
void MNS(int C[],int n,int **size) { for(int j=0;j<C[1];j++) { size[1][j]=0; } for(int j=C[1]; j<=n; j++) { size[1][j]=1; } for(int i=2; i<n; i++) { for(int j=0; j<C[i]; j++) { size[i][j]=size[i-1][j];//當i<c[i]的情形 } for(int j=C[i]; j<=n; j++) { //當j>=c[i]時,考慮(i,c[i])是否屬於MNS(i,j)的兩種情況 size[i][j]=max(size[i-1][j],size[i-1][C[i]-1]+1); } } size[n][n]=max(size[n-1][n],size[n-1][C[n]-1]+1); }
3、構造最優解
紅色標明的就是算法選擇的路徑(向上優先,也可以用向左優先,答案都是四個,但值會有一點不同)。在斜角值改變時可以取得所求的子集。即 9->10,7->9, 5->5, 3->4
void Traceback(int C[],int **size,int n,int Net[],int& m) { int j=n; m=0; for(int i=n;i>1;i--) { if(size[i][j]!=size[i-1][j])//此時,(i,c[i])是最大不相交子集的一條邊 { Net[m++]=i; j=C[i]-1;//更新擴展連線柱區間 } } if(j>=C[1])//處理i=1的情形 { Net[m++]=1; } }
4、計算復雜性
5、演示代碼
#include <iostream> using namespace std; const int N = 10; void MNS(int C[],int n,int **size); void Traceback(int C[],int **size,int n,int Net[],int& m); int main() { int c[] = {0,8,7,4,2,5,1,9,3,10,6};//下標從1開始 int **size = new int *[N+1]; for(int i=0; i<=N; i++) { size[i] = new int[N+1]; } MNS(c,N,size); cout<<"電路布線最大不相交連線數目為:"<<size[N][N]<<endl; int Net[N],m; Traceback(c,size,N,Net,m); cout<<"最大不相交連線分別為:"<<endl; for(int i=m-1; i>=0; i--) { cout<<"("<<Net[i]<<","<<c[Net[i]]<<") "; } cout<<endl; return 0; } void MNS(int C[],int n,int **size) { for(int j=0;j<C[1];j++) { size[1][j]=0; } for(int j=C[1]; j<=n; j++) { size[1][j]=1; } for(int i=2; i<n; i++) { for(int j=0; j<C[i]; j++) { size[i][j]=size[i-1][j];//當i<c[i]的情形 } for(int j=C[i]; j<=n; j++) { //當j>=c[i]時,考慮(i,c[i])是否屬於MNS(i,j)的兩種情況 size[i][j]=max(size[i-1][j],size[i-1][C[i]-1]+1); } } size[n][n]=max(size[n-1][n],size[n-1][C[n]-1]+1); } void Traceback(int C[],int **size,int n,int Net[],int& m) { int j=n; m=0; for(int i=n;i>1;i--) { if(size[i][j]!=size[i-1][j])//此時,(i,c[i])是最大不相交子集的一條邊 { Net[m++]=i; j=C[i]-1;//更新擴展連線柱區間 } } if(j>=C[1])//處理i=1的情形 { Net[m++]=1; } }