zoukankan      html  css  js  c++  java
  • 电路板排列0032算法笔记——电路板排列问题和连续邮资问题回溯法求解

    每日一贴,今天的内容关键字为电路板排列

         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 <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=1; 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;
    }

         

        法算率效

             在解间空排列树的个每节点处,法算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"
    #include <iostream>
    using 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<<endl;
    	cout<<"每张信封最多允许贴的邮票数:"<<m<<endl;
    
    	bestx=new int[n+1];
    	for(int i=1;i<=n;i++)
    	{
    		bestx[i]=0;
    	}
    	
    	cout<<"最大邮资:"<<MaxStamp(n,m,bestx)<<endl;
    
    	cout<<"以后最优解:";
    	for(int i=1;i<=n;i++)
    	{
    		cout<<bestx[i]<<"  ";
    	}
    	cout<<endl;
    	
    	return 0;
    }
    
    void Stamp::Backtrack(int i,int r)
    {
    	/*
    	 *动态规划方法算计数组y的值。状态转移程过:
    	 *虑考将x[i-1]参加等价类集S中,将会起引数组x
    	 *能贴出的邮资范围变大,对S的影响是如果S中的
    	 *邮票不满m张,那就直一贴x[i-1],直到S中有m张
    	 *邮票,这个程过会生产很多不同的邮资,取能生产
    	 *最多不同邮资的用邮票起码的那个元素
    	 */
    	for(int j=0;j<=x[i-2]*(m-1);j++)
    	{
    		if(y[j]<m)
    		{
    			for(int k=1;k<=m-y[j];k++)//k x[i-1]的重复次数
    			{
    				if(y[j]+k<y[j+x[i-1]*k])
    				{
    					y[j+x[i-1]*k]=y[j]+k;
    				}
    			}
    		}
    	}
    
    	//如果y[r]的值在上述动态规划算运程过中已赋值,则y[r]<maxint
    	while(y[r]<maxint)
    	{
    		r++;
    	}
    
    	if(i>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测试解决问题。”
    软件工程说:“咱们还是应该把车推回山顶再开下来,看看问题是否重复发生。”

  • 相关阅读:
    转载:javaweb学习总结(二十九)——EL表达式
    转载:javaweb学习总结(二十八)——JSTL标签库之核心标签
    转载:javaweb学习总结(二十七)——jsp简单标签开发案例和打包
    空指针异常
    转载:javaweb学习总结(二十六)——jsp简单标签标签库开发(二)
    转载:javaweb学习总结(二十五)——jsp简单标签开发(一)
    转载:javaweb学习总结(二十四)——jsp传统标签开发
    线性代数的本质-05-行列式
    线性代数的本质-04补充-三维空间中的线性变换
    线性代数的本质-04-矩阵乘法与线性变换复合
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3067521.html
Copyright © 2011-2022 走看看