每日一贴,今天的内容关键字为电路板排列
1、电路板排列问题
问题描述
将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},N1={4, 5, 6},N2={2, 3},N3={1, 3},N4={3, 6},N5={7, 8};其中两个可能的排列如图所示,则该电路板排列的度密分别是2,3。
左上图中,越跨插槽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#include 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 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="< <<",n="< < >B[i][j]; cout< <<" "; } cout< 0 && total[k]!=now[k]) { ld ++; } } //更新ld if(cd>ld) { ld = cd; } if(ld inline void Swap(Type &a, Type &b){ Type temp=a; a=b; b=temp;}
法算率效
在解间空排列树的个每节点处,法算Backtrack费花O(m)算计时光为个每儿子节点算计度密。因此算计度密所消费的总算计时光为O(mn!)。另外,成生排列树要需O(n!)时光。每次更新以后最优解少至使bestd增加1,而法算运行结束时bestd>=0。因此最优解被更新的额次数为O(m)。更新最优解要需O(mn)时光。综上,解电路板排列问题的溯回法算Backtrack所要需的算计时光为O(mn!)。
程序运行结果为:
2、连续邮资问题
问题描述
设假国度发行了n种不同面值的邮票,并且划定每张信封上最多只允许贴m张邮票。连续邮资问题求要对于给定的n和m的值,给出邮票面值的佳最计划,在1张信封上可贴出从邮资1开始,增量为1的最大连续邮资区间。例如,当n=5和m=4时,面值为(1,3,11,15,32)的5种邮票可以贴出邮资的最大连续邮资区间是1到70。
问题分析
解向量:用n元组x[1:n]表现n种不同的邮票面值,并约定它们从小到大排列。x[1]=1是一唯的选择。
可行性约束函数:已选定x[1:i-1],最大连续邮资区间是[1:r],接下来x[i]的可取值范围是[x[i-1]+1:r+1]。算计X[1:i]的最大连续邮资区间在本法算中被频仍应用到,因此势须要找到一个高效的方法。直接递归的求解复杂度太高,我们不妨实验算计用不超越m张面值为x[1:i]的邮票贴出邮资k所需的起码邮票数y[k]。通过y[k]可以很快推出r的值。如果y[r]的值在上述动态规划算运程过中已赋值,则y[r]<maxint。句语while(y[r]<maxint) r++可以很快的算计出r值。关键是如何算计数组y,分析程过如下:
r表现由x[1…i]能贴出的最大连续区间,当初,要想把第i层的结点往下展扩,有两个问题要需处理:一,哪些数有可能成为下一个的邮票面值,即x[i+1]的取值范围是什么;二,对于一个肯定的x[i+1],如何更新r的值让它表现x[1…i+1]能表现的最大连续邮资区间。 第一个问题很简单,x[i+1]的取值要和面前i个数各不相同,小最应该是x[i] + 1,最大就是r+1,否则r+1没有办法表现。我们当初注专第二个问题。 第二个问题自己有两种思绪:一,算计出全部应用不超越m张x[1…i+1]中的面值可以贴出的邮资,然后从r+1开始个逐查检否是被算计出来。二,从r+1开始,个逐讯问它是不是可以用不超越m张x[1…i+1]中的面值贴出来。 两种思绪直接算计其算计量都是大巨的,要需借助动态规划的方法。模拟0-1背包问题,设假S(i)表现x[1…i]中不超越m张邮票的贴法的集合,这个集合中的元素数目是大巨的,例如,只应用1张邮票的贴法有C(i+1-1,1)=C(i,1)=i种,应用2张邮票的贴法有C(i+2-1,2)=C(i+1,2)=i*(i+1)/2种,……,应用m张邮票的贴法有C(i+m-1, m)种,其中C(n,r)表现n个元素中取r个元素的组合数。于是,S(i)中的元素的数目总共有C(i+1-1, 1) + C(i+2-1,2)+ … + C(i+m-1,m)个。S(i)中的个每元素就是一种正当的贴法,对应一个邮资。以后最大连续邮资区间为1到r,那么S(i)中个每元素的邮资是不是也在1到r之间呢?不一定,比如{1,2,4},当m=2时,它能贴出来8,但不能贴出来7。总之,在索搜时,一定要坚持状态的一致性,即当深度索搜到第i层时,一定要确保用来存保结点状态的变量中存保的一定是第i层的这个结点的状态。定义S(i)中元素的值就是它所表现的贴法贴出来的邮资,于是,可以把S(i)中的元素按照它们的值的等相关系成分k类。第j类表现贴出邮资为j的全部的贴法集合,用T(j)表现,T(j)有是能可空集,例如对于{1,2,4},T(7)为空集,T(8)={ {4,4}}。此时有:S(i) = T(1) U T(2) U T(3) U … U T(k),U表现两个集合的并。 当初虑考x[i+1]参加后对以后状态S(i)的影响。设假s是S(i)中的一个元素,即s表现一种正当的贴法,x[i+1]对s能贴出的邮资的影响就是x[i+1]的多次重复增加了s能贴出的邮资。x[i+1]对s的影响就是,如果s中贴的邮票不满m张,那就直一贴x[i+1],直到s中有m张邮票,这个程过会生产出很多不同的邮资,它们都应该被参加到S(i+1)中。因为s属于S。
综上分析,虑考如果应用动态规划方法算计数组y的值,状态转移程过:将x[i-1]参加等价类集S中,将会起引数组x能贴出的邮资范围变大,对S的影响是如果S中的邮票不满m张,那就直一贴x[i-1],直到S中有m张邮票,这个程过会生产很多不同的邮资,取能生产最多不同邮资的用邮票起码的那个元素。
例如:如下图所示,设m=4,n=5。当x[1]=1时,2张{1,1}可以贴出邮资2。这时,设x[2]=3。将3往{1,1}中添加,生产新的邮资贴法:5:{3,1,1},8:{3,3,1,1}。这时,程序要需更新数组y的值。如果新的贴法比y[5],y[8]已有的贴法所用的张数更少,则更新之。
法算体具实现如下:
//连续邮资问题 溯回法求解#include "stdafx.h"#includeusing namespace std;class Stamp{ friend int MaxStamp(int ,int ,int []); private: int Bound(int i); void Backtrack(int i,int r); int n;//邮票面值数 int m;//每张信封最多允许贴的邮票数 int maxvalue;//以后最优值 int maxint;//大整数 int maxl;//邮资上界 int *x;//以后解 int *y;//贴出各种邮资所需起码邮票数 int *bestx;//以后最优解};int MaxStamp(int n,int m,int bestx[]);int main(){ int *bestx; int n = 5; int m = 4; cout<<"邮票面值数:"< < n) { if(r-1>maxvalue) { maxvalue=r-1; for(int j=1;j<=n;j++) { bestx[j]=x[j]; } } return; } int *z=new int[maxl+1]; for(int k=1;k<=maxl;k++) { z[k]=y[k]; } for(int j=x[i-1]+1;j<=r;j++) { x[i]=j; Backtrack(i+1,r); for(int k=1;k<=maxl;k++) { y[k]=z[k]; } } delete[] z;}int MaxStamp(int n,int m,int bestx[]){ Stamp X; int maxint=32767; int maxl=1500; X.n=n; X.m=m; X.maxvalue=0; X.maxint=maxint; X.maxl=maxl; X.bestx=bestx; X.x=new int [n+1]; X.y=new int [maxl+1]; for(int i=0;i<=n;i++) { X.x[i]=0; } for(int i=1;i<=maxl;i++) { X.y[i]=maxint; } X.x[1]=1; X.y[0]=0; X.Backtrack(2,1); delete[] X.x; delete [] X.y; return X.maxvalue;}
程序运行结果如图:
文章结束给大家分享下程序员的一些笑话语录: 据说有一位软件工程师,一位硬件工程师和一位项目经理同坐车参加研讨会。不幸在从盘山公路下山时坏在半路上了。于是两位工程师和一位经理就如何修车的问题展开了讨论。
硬件工程师说:“我可以用随身携带的瑞士军刀把车坏的部分拆下来,找出原因,排除故障。” 项目经理说:“根据经营管理学,应该召开会议,根据问题现状写出需求报告,制订计划,编写日程安排,逐步逼近,alpha测试,beta1测试和beta2测试解决问题。” 软件工程说:“咱们还是应该把车推回山顶再开下来,看看问题是否重复发生。”