1.题目
在一块电路板的上、下2端分别有n个接线柱。根据电路设计,要求用导线(i,π(i))将上端接线柱与下端接线柱相连,确定将哪些连线安排在第一层上,使得该层上有尽可能多的连线。该问题要求确定导线集Nets={(i,π(i)),1≤i≤n}的最大不相交子集。
2.分析
1.题目分析
首先程序需求是要求我们在输入一个已经布线好的电路板上寻找一个相互不循环的电线最大子集,那么直接从整体去分析显然比较困难。我们不妨将问题逐个划分为一个一个小问题,同时也很明显的能看出经过分解后的小问题最小能够划分一条线,而线与线之间存在相交关系,那么子问题之间就一定不是单纯的相互独立问题,如果能保存下每一步的计算结果,用于比较其他情况,这样可避免过多计算,各个数据间的关系也能很好体现。
所以我们采用动态规划方法
2.数据结构
一块电路板上下端分别有n个接线柱,根据电路设计,用导线(i,e[i])将上端接线柱与下端接线柱e[i]相连,所以e是个数组,(i,e[i])表示第i条线,对于任何1<=i<j<=n,第i条线和第j条线相交的充分必要条件e[i]>e[j]。
制作电路板要求将这n条导线分配到若干绝缘层上,每一层的导线都互不相交,电路布线问题就是要确定哪些导线安排在第一层,使该层有尽可能多的导线,即求导线集{(i,e[i])}的最大不相交子集。
故声明数组定义如下:
dp[i][j]: 在上端接线柱从0至i,下端接线柱从0至j,最大不相交子集的元素数量
e[i]:上端接线柱i连接的下端接线柱
z[i]:最大不相交子集
3.动态规划
1.填表
2.回溯
1.从最后一个开始,判断是否等于上一个(寻找最大不相交子集里的最后一条线的上接线柱)
2.等于的话,就继续往上找
3.不等于的话记录这个这个上接线柱序号i,并于上一行第c[i]-1列继续执行1~3步。
(缩小寻找的范围,在(1,c[i]-1)内寻找,是为了避免找到的线与已找到的线相交)
4.算法代码
1.递归
#include<iostream>
using namespace std;
void Buxian(int** dp, int* e, int n, int i)
{
int a;
if (i == 1)
{
for (a = 0; a < e[1]; a++)
dp[1][a] = 0;
for (a = e[1]; a <= n; a++)
dp[1][a] = 1;
}
else
{
Buxian(dp, e, n, i - 1);
for (a = 0; a < e[i]; a++)
dp[i][a] = dp[i - 1][a];
for (a = e[i]; a <= n; a++)
{
if (dp[i - 1][a] > dp[i][e[i] - 1] + 1)
dp[i][a] = dp[i - 1][a];
else
dp[i][a] = dp[i][e[i] - 1] + 1;
}
}
}
void jihe(int** dp, int* e, int* z, int n)
{
int a, b;
b = n;
int g = 0;
for (a = n; a > 1; a--)
{
if (dp[a][b] != dp[a - 1][b])
{
z[g++] = a;
b = e[a] - 1;
}
}
if (b >= e[1])
z[g++] = 1;
}
int main()
{
int a, b, c, d, n;
int* e;
int* z;
int** dp;
cout << "请输入有多少个端点:";
cin >> n;
z = new int[n + 1];
e = new int[n + 1];
dp = new int* [n + 1];
for (a = 1; a <= n; a++)
dp[a] = new int[n + 1];
cout << "请按照顺序输入所连接的下端点" << endl;
for (a = 1; a <= n; a++)
{
cin >> e[a];
}
Buxian(dp, e, n, n);
cout << "最大不相交子集中有" << dp[n][n] << "条线" << endl;
jihe(dp, e, z, n);
cout << "为:" << endl;
for (a = 0; a < dp[n][n]; a++)
{
cout << "(" << z[a] << "," << e[z[a]] << ")" << endl;
}
}
2.循环
#include<iostream>
using namespace std;
void Buxian(int** dp, int* e, int n)
{
int a, i;
for (a = 0; a < e[1]; a++)
dp[1][a] = 0;
for (a = e[1]; a <= n; a++)
dp[1][a] = 1;
for (i = 2; i <= n; i++)
{
for (a = 0; a < e[i]; a++)
dp[i][a] = dp[i - 1][a];
for (a = e[i]; a <= n; a++)
{
if (dp[i - 1][a] > dp[i][e[i] - 1] + 1)
dp[i][a] = dp[i - 1][a];
else
dp[i][a] = dp[i][e[i] - 1] + 1;
}
}
}
void jihe(int** dp, int* e, int* z, int n)
{
int a, b;
b = n;
int g = 0;
for (a = n; a > 1; a--)
{
if (dp[a][b] != dp[a - 1][b])
{
z[g++] = a;
b = e[a] - 1;
}
}
if (b >= e[1])
z[g++] = 1;
}
int main()
{
int a, b, c, d, n;
int* e;
int* z;
int** dp;
cout << "请输入有多少个端点:";
cin >> n;
z = new int[n + 1];
e = new int[n + 1];
dp = new int* [n + 1];
for (a = 1; a <= n; a++)
dp[a] = new int[n + 1];
cout << "请按照顺序输入所连接的下端点" << endl;
for (a = 1; a <= n; a++)
{
cin >> e[a];
}
Buxian(dp, e, n);
cout << "最大不相交子集中有" << dp[n][n] << "条线" << endl;
jihe(dp, e, z, n);
cout << "为:" << endl;
for (a = 0; a < dp[n][n]; a++)
{
cout << "(" << z[a] << "," << e[z[a]] << ")" << endl;
}
}
3.结果
5.算法分析
1.电路布线动态规划算法:
void Buxian(int** dp, int* e, int n)
{
int a,i;
for (a = 0;a < e[1];a++)
dp[1][a] = 0;
for (a = e[1];a <= n;a++)
dp[1][a] = 1;
for (i = 2;i <= n;i++)
{
for (a = 0;a < e[i];a++)
dp[i][a] = dp[i - 1][a];
for (a = e[i];a <= n;a++)
{
if (dp[i - 1][a] > dp[i][e[i] - 1] + 1)
dp[i][a] = dp[i - 1][a];
else
dp[i][a] = dp[i][e[i] - 1] + 1;
}
}
}
时间复杂度:O(n2)
空间复杂度:O(n2)
2.构造最优解
void jihe(int** dp, int* e, int* z, int n)
{
int a, b;
b = n;
int g=0;
for (a = n;a > 1;a--)
{
if (dp[a][b] != dp[a - 1][b])
{
z[g++] = a;
b = e[a] - 1;
}
}
if (b >= e[1])
z[g++] = 1;
}
时间复杂度:O(n)
空间复杂度:O(n2)
6.算法应用
集成电路是利用半导体技术把电子元件集成在一起的具有特定功能的电路, 已广泛应用于生产生活的方方面面。随着技术的发展,集成电路内部的元器件数目已达到十亿级别,需要借助专用计算机软件才能完成电路设计与实现,该类软件统称为电子设计自动化(Electronic Design Automation, EDA)工具。
集成电路设计由多个阶段组成,其中一个重要阶段称为“物理设计”,先将器件摆放在合适的位置,然后用金属线连接器件实现连接关系。其中,后者称为“布线”,它是 EDA 工具需要解决的重要问题。简单而言,假设可用区域由 m*n个方格组成,金属线允许沿着直线或直角(方格)放置,连接指定的方格(引脚)而不引起断路或短路,该过程称为“布线”。由于金属线引入的寄生电阻会影响 电路性能,所以需要最小化布线长度。
本问题重点考虑 “布线”问题中的一个特例:“通道布线”。“通道”是指一个横向的布线区域,此区域的顶部和底部分布着需要连接的方格,需用金属线将相应的引脚连通起来。