回溯法之電路板排列問題


問題描述

     將n塊電路板以最佳排列方式插入帶有n個插槽的機箱中。n塊電路板的不同排列方式對應於不同的電路板插入方案。設B={1, 2, …, n}是n塊電路板的集合,集合L={N1, N2, …, Nm}是連接這n塊電路板中若干電路板的m個連接塊。其中,每個連接塊Ni是B的一個子集,且Ni中的電路板用同一條導線連接在一起。設x表示n塊電路板的一個排列,即在機箱的第i個插槽中插入的電路板編號是x[i]。x所確定的電路板排列Density (x)密度定義為跨越相鄰電路板插槽的最大連線數。

    例:如圖,設n=8, m=5,給定n塊電路板及其m個連接塊:B={1, 2, 3, 4, 5, 6, 7, 8},L={N1,N2,N3,N4,N5},N1={4, 5, 6},N2={2, 3},N3={1, 3},N4={3, 6},N5={7, 8};其中可能的排列如圖所示,則該電路板排列的密度是2。

       

     上圖中,跨越插槽2和3,4和5,以及插槽5和6的連線數均為2。插槽6和7之間無跨越連線。其余插槽之間只有1條跨越連線。在設計機箱時,插槽一側的布線間隙由電路板的排列的密度確定因此,電路板排列問題要求對於給定的電路板連接條件(連接塊),確定電路板的最佳排列,使其具有最小密度。

 問題分析

     電路板排列問題是NP難問題,因此不大可能找到解此問題的多項式時間算法。考慮采用回溯法系統的搜索問題解空間的排列樹,找出電路板的最佳排列。算法中用整型數組B表示輸入。B[i][j]的值為1當且僅當電路板i在連接塊Nj中。設total[j]是連接塊Nj中的電路板數。對於電路板的部分排列x[1:i],設now[j]是x[1:i]中所包含的Nj中的電路板數。由此可知,連接塊Nj的連線跨越插槽i和i+1當且僅當now[j]>0且now[j]!=total[j]。用這個條件來計算插槽i和i+1間的連線密度。

算法具體實現如下:

//電路板排列問題 回溯法求解
#include "stdafx.h"
#include <iostream>
#include <fstream> 
using namespace std;
 
ifstream fin("5d11.txt"); 
 
class Board
{
    friend int Arrangement(int **B, int n, int m, int bestx[]);
    private:
        void Backtrack(int i,int cd);
        int n,        //電路板數
            m,        //連接板數
            *x,        //當前解
            *bestx,//當前最優解
            bestd,  //當前最優密度
            *total, //total[j]=連接塊j的電路板數
            *now,   //now[j]=當前解中所含連接塊j的電路板數
            **B;    //連接塊數組
};
 
template <class Type>
inline void Swap(Type &a, Type &b);
 
int Arrangement(int **B, int n, int m, int bestx[]);
 
int main()
{
    int m = 5,n = 8;
    int bestx[9];
 
    //B={1,2,3,4,5,6,7,8}
    //N1={4,5,6},N2={2,3},N3={1,3},N4={3,6},N5={7,8}
 
    cout<<"m="<<m<<",n="<<n<<endl;
    cout<<"N1={4,5,6},N2={2,3},N3={1,3},N4={3,6},N5={7,8}"<<endl;
    cout<<"二維數組B如下:"<<endl;
 
    //構造B
    int **B = new int*[n+1];
    for(int i=1; i<=n; i++)
    {
        B[i] = new int[m+1];
    }
 
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m ;j++)
        {
            fin>>B[i][j];
            cout<<B[i][j]<<" ";
        }
        cout<<endl;
    }
 
    cout<<"當前最優密度為:"<<Arrangement(B,n,m,bestx)<<endl;
    cout<<"最優排列為:"<<endl;
    for(int i=1; i<=n; i++)
    {
        cout<<bestx[i]<<" ";
    }
    cout<<endl;
 
    for(int i=1; i<=n; i++)
    {
        delete[] B[i];
    }
    delete[] B;
 
    return 0;
}
 
void Board::Backtrack(int i,int cd)//回溯法搜索排列樹
{
    if(i == n)
    {
        for(int j=1; j<=n; j++)
        {
            bestx[j] = x[j];
        }
        bestd = cd;
    }
    else
    {
        for(int j=i; j<=n; j++)
        {
            //選擇x[j]為下一塊電路板
            int ld = 0;
            for(int k=1; k<=m; k++)
            {
                now[k] += B[x[j]][k];
                if(now[k]>0 && total[k]!=now[k])
                {
                    ld ++;
                }
            }
 
            //更新ld
            if(cd>ld)
            {
                ld = cd;
            }
 
            if(ld<bestd)//搜索子樹
            {
                Swap(x[i],x[j]);
                Backtrack(i+1,ld);
                Swap(x[i],x[j]);
 
                //恢復狀態
                for(int k=1; k<=m; k++)
                {
                    now[k] -= B[x[j]][k];
                }
            }
        }
    }    
}
 
int Arrangement(int **B, int n, int m, int bestx[])
{
    Board X;
 
    //初始化X
    X.x = new int[n+1];
    X.total = new int[m+1];
    X.now = new int[m+1];
    X.B = B;
    X.n = n;
    X.m = m;
    X.bestx = bestx;
    X.bestd = m+1;
 
    //初始化total和now
    for(int i=1; i<=m; i++)
    {
        X.total[i] = 0;
        X.now[i] = 0;
    }
 
 
    //初始化x為單位排列並計算total
    for(int i=1; i<=n; i++)
    {
        X.x[i] = i;
        for(int j=1; j<=m; j++)
        {
            X.total[j] += B[i][j];
        }
    }
 
    //回溯搜索
    X.Backtrack(1,0);
    delete []X.x;
    delete []X.total;
    delete []X.now;
    return X.bestd;
}
 
template <class Type>
inline void Swap(Type &a, Type &b)
{  
    Type temp=a; 
    a=b; 
    b=temp;
}
View Code

實現結果:

 算法效率:

     在解空間排列樹的每個節點處,算法Backtrack花費O(m)計算時間為每個兒子節點計算密度。因此計算密度所消耗的總計算時間為O(mn!)。另外,生成排列樹需要O(n!)時間。每次更新當前最優解至少使bestd減少1,而算法運行結束時bestd>=0。因此最優解被更新的額次數為O(m)。更新最優解需要O(mn)時間。綜上,解電路板排列問題的回溯算法Backtrack所需要的計算時間為O(mn!)。

轉載:https://blog.csdn.net/liufeng_king/article/details/8898372


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM