本次实验,我选择课本P156页5-2作为实验报告。
(1) 首先是问题描述:
最小长度电路板排列问题是大规模电子系统设计中提出的实际问题。该问题的提法是,将n块电路板以最佳排列方案插入带有n个插槽的机箱中。n块电路板的不同的排列方式对应于不同的电路板插入方案。
设B={1,2,…,n }是n块电路板的集合。集合L={ N1,N2,…,Nm }是n块电路 板的m个连接块。其中每个连接块Ni是B的一个子集,且Ni中的电路板用同一根导线连 接在一起。
例如,设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}。
这8块电路板的一个可能的排列如图所示。
则该电路板排列的密度分别是2,3。
左上图中,跨越插槽2和3,4和5,以及插槽5和6的连线数均为2。插槽6和7之间无跨越连线。其余插槽之间只有1条跨越连线。在设计机箱时,插槽一侧的布线间隙由电路板的排列的密度确定。因此,电路板排列问题要求对于给定的电路板连接条件(连接块),确定电路板的最佳排列,使其具有最小密度。
(2) 问题分析
电路板排列问题是NP难问题,因此不大可能找到解此问题的多项式时间算法。考虑采用回溯法系统的搜索问题解空间的排列树,找出电路板的最佳排列。排列树问题。对所有的电路板进行全排列,每次找到连接块的最左和最右电路板,相减得到连接块的长度,在全排列的过程中找到最小长度。
(3) 代码描述
//最小电路板排列问题
#include<iostream>
using namespace std;
const int maxn = 10000;
int originalxu[maxn];//保留初始排序
int finalxu[maxn];//保留处理后的排序
int n;//讲变量n分配到全局,方便后续的使用
int min_len=INT32_MAX; //存储最终的最短的长度
int temp_len ;//题目中临时用到的长度
int e_sum[maxn][maxn];
int m;
void traceback(int t)//构造回溯函数
{
int left,right;//定义一个左右节点。
int temp;//@to_do 在后续的交换操作中会用到;
if(t==n)
{
temp_len=0;//在此处赋予临时变量的值为0,如果此函数内部不赋值temp_len可能会导致全局失效。
for(int i=0;i<m;i++)//从连接块开始遍历
{
for(int j=0;j<n;j++)//接着进行电路板的遍历
{
if(e_sum[originalxu[j]][i]==1)//左边的孩子
{
left = j;//TODO j和i的顺序不要搞反了;注意这个地方
break;
}
}
for(int j=n-1;j>=0;j--)
{
if(e_sum[originalxu[j]][i]==1)//右边的孩子
{
right = j;
break;
}
}
if(temp_len<right-left)
{
temp_len = right-left;//记录最大长度;
}
}
if(temp_len<min_len) {
min_len = temp_len; //记录最大长度
for(int i=0;i<n;i++) {
finalxu[i] = originalxu[i];//把顺序付给新的变量;
}
}
}
for(int i=t;i<n;i++)
{
temp = originalxu[i];
originalxu[i]=originalxu[t];
originalxu[t]=temp;
traceback(t+1);
temp = originalxu[i];
originalxu[i]=originalxu[t];
originalxu[t]=temp;
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>e_sum[i][j]; //不妨先把变量传递进来
}
}
for(int t=0;t<n;t++){
originalxu[t] = t;
}
traceback(0);
cout<<min_len<<endl;
for(int r=0;r<n;r++)
{
cout<<finalxu[r]+1<<" ";
}
cout<<endl;
(四) 总结
在写代码的时候,由于不能很好地给比变量进行命名导致某些地方可能一直出错,此外,要理清各个函数之间的关系,不能混淆了i和j,另外临时变量和全局变量一定要搞清楚,有时候在函数内部赋予局部变量会出现一些莫名其妙的bug。
顺便总结一下回溯的模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}