“王晓东”算法设计之回溯与分支限界法经典习题总结&AC代码(C++)

1. 最小长度电路板排列问题

【问题描述】
最小长度电路板排列问题是大规模电子系统设计中提出的实际问题。该问题的提法是, 将 n 块电路板以最佳排列方案插入带有 n 个插槽的机箱中。n 块电路板的不同的排列方式对 应于不同的电路板插入方案。 设 B={1,2,…,n }是 n 块电路板的集合。集合 L={ N1, N2 ,…, N m }是 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 块电路板的一个可能的排列如图所示。
在这里插入图片描述
在最小长度电路板排列问题中,连接块的长度是指该连接块中第 1 块电路板到最后 1 块电路板之间的距离。例如在图示的电路板排列中,连接块 N4 的第 1 块电路板在插槽 3 中, 它的最后 1 块电路板在插槽 6 中,因此 N4 的长度为 3。同理 N2 的长度为 2。图中连接块最 大长度为 3。试设计一个分支限界法找出所给 n 个电路板的最佳排列,使得 m 个连接块中最 大长度达到最小。
对于给定的电路板连接块,设计一个队列式分支限界法,找出所给 n 个电路板的最佳排 列,使得 m 个连接块中最大长度达到最小。

【输入形式】
第一行有 2 个正整数 n 和 m (1≤m,n≤20)。接下来的 n 行中,每行有 m 个数。第 k 行的第 j 个数为 0 表示电路板 k 不在连接块 j 中,1 表示电路板 k 在连接块 j 中。
【输出形式】
将计算出的电路板排列最小长度及其最佳排列输出。
文件的第 1 行是 最小长度;接下来的 1 行是最佳排列。

【样例输入】
8 5
1 1 1 1 1
0 1 0 1 0
0 1 1 1 0
1 0 1 1 0
1 0 1 0 0
1 1 0 1 0
0 0 0 0 1
0 1 0 0 1
【样例输出】
4
5 4 3 1 6 2 8 7

/*最小长度电路板排列问题*/
#pragma GCC optimize(2 , "Ofast" , "inline")
#include <bits/stdc++.h>
using namespace std;
 
int n,m,**arr,*a;
int minlength=100000,templength;
int lef,rig,*opt;
 
int dfs(int t)
{
    
    
	int i,j,temp;
	
	if(t==n)
	{
    
    
		templength = 0;
		for(i=0; i<m; i++)
		{
    
    
			for(j=0; j<n; j++) 
				if(arr[a[j]][i] == 1) {
    
    lef = j;	break;}
			for(j=n-1; j>=0; j--) 
				if(arr[a[j]][i] == 1) {
    
    rig = j;	break;}
		    if(templength<rig-lef)    templength = rig - lef;
		}
		if(minlength>templength) {
    
    
			minlength = templength;
			for(i=0; i<n; i++)	opt[i] = a[i]+1;
		}
	}
	
	for(i=t; i<n; i++)
	{
    
    
		temp = a[i];
		a[i] = a[t];
		a[t] = temp;
		dfs(t+1);
		temp = a[i];
		a[i] = a[t];
		a[t] = temp;
	}
	return 0;
}
 
 
int main()
{
    
    
	scanf("%d %d",&n,&m);
	a = (int*)malloc(n*sizeof(int));
	opt = (int*)malloc(n*sizeof(int));
	arr = (int **) malloc(n*sizeof(int *));//申请一组一维指针空间。
    
	for(int i=0; i<n; i++)
        arr[i] = (int *)malloc(m*sizeof(int)); //对于每个一维指针,申请一行数据的空间。
    
	for(int i=0; i<n; i++)	a[i] = i;
	
	for(int i=0; i<n; i++)
		for(int j=0; j<m; j++)	scanf("%d",&arr[i][j]);	
	
	dfs(0);
	printf("%d\n",minlength);
	for(int i=0; i<n; i++)	printf("%d ",opt[i]);
	return 0;
}

2. 圆排列问题

在这里插入图片描述

算法设计:
对于给定的 n 个圆,设计一个优先队列式分支限界法,计算 n 个圆的最佳排列方案,使其长度达到最小。

【输入形式】
输入数据。第一行有 1 个正整数 n (1≤n≤20)。接下来的 1 行有 n个数,表示 n 个圆的半径。
【输出形式】
将计算出的最小圆排列的长度输出。

【样例输入】
3
1 1 2
【样例输出】
7.65685

#include <bits/stdc++.h>
using namespace std;

const int MAX_SIZE=100;
int N;
double minlen = 10000, x[MAX_SIZE], r[MAX_SIZE];//分别为最小圆排列长度,每个圆心横坐标,每个圆半径
double bestr[MAX_SIZE];//最小圆排列的半径顺序

//计算圆心横坐标
double center(int t)//得到每个圆的圆心坐标
{
    
    
	double temp = 0;
	for (int j=1; j<t; ++j)//因为目标圆有可能与排在它之前的任一圆相切,故需一一判断
	{
    
    
		double xvalue = x[j] + 2.0*sqrt(r[t]*r[j]);
		temp = max(xvalue, temp);
	}
	return temp;
}

//计算圆排列长度
void compute()
{
    
    
	double low=0, high=0;
	for(int i=1; i<N; ++i)
	{
    
    
		low = min(low, x[i]-r[i]);
		high = max(x[i] + r[i], high);
	}
	if(high-low < minlen)
	{
    
    
		minlen = high - low;
		for (int i=1; i<N; ++i)	bestr[i] = r[i];
	}
}

//回溯算法
void backtrack(int t)
{
    
    
	if (t == N)
	{
    
    
		compute();//计算圆排列长度
		return;
	}
	for (int j = t; j < N; ++j)
	{
    
    
		swap(r[t], r[j]);
		double centerx = center(t);
		if (centerx + r[t] + r[1] < minlen)
		{
    
    
			x[t] = centerx;
			backtrack(t + 1);//到第t+1个圆
		}
		swap(r[t], r[j]);//恢复状态
	}
}

int main()
{
    
    
	cin >> N;
	N++;
	for(int i=1; i<N; i++)	cin >> r[i];
	backtrack(1);
	cout << minlen << endl;
	return 0;
}

3. 推箱子问题

【问题描述】
码头仓库是划分为n×m个格子的矩形阵列。有公共边的格子是相邻格子。当前仓库中有的格子是空闲的;有的格子则已经堆放了沉重的货物。由于堆放的货物很重,单凭仓库管理员的力量是无法移动的。仓库管理员有一项任务,要将一个小箱子推到指定的格子上去。管理员可以在仓库中移动,但不能跨过已经堆放了货物的格子。管理员站在与箱子相对的空闲格子上时,可以做一次推动,把箱子推到另一相邻的空闲格子。推箱时只能向管理员的对面方向推。由于要推动的箱子很重,仓库管理员想尽量减少推箱子的次数。
对于给定的仓库布局,以及仓库管理员在仓库中的位置和箱子的开始位置和目标位置,设计一个解推箱子问题的分支限界法,计算出仓库管理员将箱子从开始位置推到目标位置所需的最少推动次数。

【输入形式】
输入数据的第1 行有2个正整数n和m(1<=n,m<=100),表示仓库是n×m个格子的矩形阵列。接下来有n行,每行有m个字符,表示格子的状态。
S 表示格子上放了不可移动的沉重货物;
w 表示格子空闲;
M 表示仓库管理员的初始位置;
P 表示箱子的初始位置;
K 表示箱子的目标位置。
【输出形式】
将计算出的最少推动次数输出。如果仓库管理员无法将箱子从开始位置推到目标位置则输出“No solution!”。

【样例输入】
10 12
SSSSSSSSSSSS
SwwwwwwwSSSS
SwSSSSwwSSSS
SwSSSSwwSKSS
SwSSSSwwSwSS
SwwwwwPwwwww
SSSSSSSwSwSw
SSSSSSMwSwww
SSSSSSSSSSSS
SSSSSSSSSSSS
【样例输出】
7

/*推箱子问题*/
#include <bits/stdc++.h> 
#define ll long long 
#include <queue> 
using namespace std; 

int n,m,manx,many,sx,sy,ex,ey; 
char x; 
int tu[102][102]; 
int steps[102][102][4]; 
bool usedmark[102][102][4];
 
struct yz {
    
     
    int x,y,type; 
	bool friend operator < (yz x,yz y) {
    
     
	    int d1=abs(x.x-ex)+abs(x.y-ey)+steps[x.x][x.y][x.type];
        int d2=abs(y.x-ex)+abs(y.y-ey)+steps[y.x][y.y][y.type]; 
	    return d1>d2; 
	} 
}q[100001],p[10001];

bool in_it(int x,int y) {
    
     
    if(x<=0 || x>n) return false; 
	if(y<=0 || y>m) return false; 
	return true; 
}

int oneNumInBinary(ll n) {
    
     // 求十进制数的二进制中1的个数
    ll cnt=0;
    while(n)  n=n&(n-1),cnt++;
    // 或者 while(n)  cnt+=n%2, n/=2; 容易理解一些 
    
    return cnt;
}

int dx[4]={
    
    0,-1,0,1}; 
int dy[4]={
    
    -1,0,1,0}; 
bool mark[102][102]; 
priority_queue<yz> que; 

void read(ll &x){
    
    
    char ch=getchar(); 
	x=0;
    for (; ch<'0' || ch>'9'; ch=getchar());
    for (; ch>='0' && ch<='9'; ch=getchar()) x=x*10+ch-'0';
}

void initial_steps(int sx,int sy,int boxx,int boxy ) {
    
     
    memset(usedmark,false,sizeof(usedmark)); 
    memset(steps,63,sizeof(steps)); 
    int cnt=1; 
	q[cnt].x=sx; 
    q[cnt].y=sy; 
    int l=1,r=1,nowx,nowy;
    memset(mark,false,sizeof(mark)); 
    mark[sx][sy]=true; 
    mark[boxx][boxy]=true; 
    while(l<=r) {
    
     
	    for(int i=0; i<4; i++) {
    
     
		    nowx=q[l].x+dx[i]; 
			nowy=q[l].y+dy[i]; 
			if(!tu[nowx][nowy] && in_it(nowx,nowy) && !mark[nowx][nowy]) {
    
     
			    mark[nowx][nowy]=true; 
				q[++r].x=nowx; q[r].y=nowy; 
			} 
		}
		l++; 
	}
	for(int i=0; i<4; i++) {
    
     
	    nowx=boxx+dx[i]; 
	    nowy=boxy+dy[i]; 
	    if(!tu[nowx][nowy] && in_it(nowx,nowy) && mark[nowx][nowy]) 
	        steps[boxx][boxy][i]=0; 
	} 
}

ll qpow(ll a,ll b) {
    
    
    ll ans=1;    
	while(b) {
    
           
	    if(b&1) ans=(ans*a)%999;        
		a=a*a%999;      
		b>>=1;    
	}   
	return ans;
}

void inter_bfs(int sx,int sy,int type,int &righ,int step) {
    
     
    if(usedmark[sx][sy][type]) return; 
	usedmark[sx][sy][type]=true; 
	int l=1,r=1; 
	int nowx=sx+dx[type]; 
	int nowy=sy+dy[type]; 
	p[l].x=nowx; 
	p[l].y=nowy; 
	memset(mark,false,sizeof(mark)); 
	mark[nowx][nowy]=true; 
	mark[sx][sy]=true; 
	while(l<=r) {
    
    
        for(int i=0; i<4; i++) {
    
     
		    nowx=p[l].x+dx[i]; 
		    nowy=p[l].y+dy[i]; 
		    if(in_it(nowx,nowy) && !mark[nowx][nowy] && !tu[nowx][nowy]) {
    
     
		        r++; 
		        mark[nowx][nowy]=true; 
		        p[r].x=nowx; p[r].y=nowy; 
		    } 
	    }l++; 
    }
    yz w; 
	for(int i=0; i<4; i++) {
    
     
	    if(i!=type) {
    
     
		    nowx=sx+dx[i]; 
		    nowy=sy+dy[i]; 
		    if(in_it(nowx,nowy) && mark[nowx][nowy] && !tu[nowx][nowy]) {
    
     
		        if(step<steps[sx][sy][i]) {
    
     
		            steps[sx][sy][i]=step; 
		            w.x=sx; 
	        	    w.y=sy; 
		            w.type=i; 
		            que.push(w); 
		            usedmark[sx][sy][i]=true; 
		        } 
			}
		} 
	}
	return; 
}

void bfs() {
    
     
    int l=1,r=0,lx,ly,ltype; 
	yz w; 
	for(int i=0; i<4; i++)
	{
    
     
	    if(steps[sx][sy][i]==0 && in_it(sx+dx[i],sy+dy[i])) {
    
     
		    w.x=sx; 
			w.y=sy; 
			w.type=i; 
			que.push(w); 
		} 
	}
	int nowx,nowy; 
	while(que.size()) {
    
     
	    yz tp=que.top(); 
	    que.pop(); 
	    lx=tp.x; 
	    ly=tp.y; 
    	ltype=tp.type; 
    	nowx=lx-dx[ltype]; 
    	nowy=ly-dy[ltype]; 
    	if(in_it(nowx,nowy) && !tu[nowx][nowy] && steps[lx][ly] [ltype]+1<=steps[nowx][nowy][ltype]) {
    
     
        	w.x=nowx; 
        	w.y=nowy; 
        	w.type=ltype;
            que.push(w); 
	        steps[nowx][nowy][ltype]=steps[lx][ly][ltype]+1; 
	        if(nowx==ex && nowy==ey) {
    
     
			    cout<<steps[nowx][nowy][ltype]<<endl; 
				return; 
	        } 
		}
		inter_bfs(lx,ly,ltype,r,steps[lx][ly][ltype]); 
		l++;
	}
    cout<<"No solution!"<<endl; 
}

int main()
{
    
     
    cin>>n>>m; 
    string s; 
    for(int i=1; i<=n; i++) {
    
     
        cin>>s; 
        for(int j=0; j<m; j++) {
    
     
            x=s[j]; 
            if(x=='S')    tu[i][j+1]=1; 
            else if(x=='M') {
    
     
                manx=i; 
                many=j+1; 
            }
            else if(x=='P') {
    
     
                sx=i; 
                sy=j+1; 
            }
            else if(x=='K') {
    
     
                ex=i; 
                ey=j+1; 
            } 
		} 
	}
	initial_steps(manx,many,sx,sy); 
	bfs(); 
	return 0; 
}

猜你喜欢

转载自blog.csdn.net/Luoxiaobaia/article/details/109725604