回溯算法,模板,拔河,旅行商,连续邮资问题题解

回溯算法模板

递归回溯

回溯法对解空间做深度有限搜索,因此在一般情况下可用递归函数来实现回溯法如下:

模板:

void backtrace(int t){
    
    
    if(t>n)
        output(x);
    else{
    
    
        for(int i=f(n,t);i<=g(n,t);i++){
    
    
            x[t]=i;
            if(constraint(t)&&bound(t)){
    
    
                backtrace(t+1);
            }
        }
    }
}

迭代回溯

采用树的非递归深度优先遍历算法,也可将回溯法便是为一个非递归的迭代过程如下:

模板:

void backtrace(int t){
    
    
    if(t>n){
    
    
        output();
    }
    while(t<n){
    
    
        if(f(n,t)<=g(n,t)){
    
    
            for(int i=f(n,t);i<=g(n,t);i++){
    
    
                x[t]=h(i);
                if(constraint(t)&&bound(t)){
    
    
                    output(x);
                }
                else
                    t++;
            }
        }
        else{
    
    
            t--;
        }
    }
}

子集树

模板:

void backtrace(int t){
    
    
    if(t>n){
    
    
        output();
    }
    else{
    
    
        for(int i=0;i<=1;i++){
    
    
            x[t]=i;
            if(constraint(t)&&bound(t)){
    
    
                backtrace(t+1);
            }
        }
    }
}

排列树

模板:

void backtrace(int t){
    
    
    if(t>n){
    
    
        output();
    }
    else{
    
    
        for(int i=t;i<=n;i++){
    
    
            swap(x[i],x[t]);
            if(constraint(t)&&bound(t)){
    
    
                backtrace(t+1);
            }
            swap(x[i],x[t]);
        }
    }
}

题目

拔河问题

#include<iostream>

using namespace std;

#define N 100

int w[N],n,totalw,tempw,temp_person_num,x[N];

void swap(int *a,int *b){
    
    
    int temp=*a;
    *a=*b;
    *b=temp;
}

//12
//48 43 57 64 50 52 18 34 39 56 16 75



void backtrace(int k){
    
    
    int i;
    if(k>n/2){
    
    
        if(tempw==totalw/2){
    
    
//            cout<<"tempw="<<tempw<<endl;
            for(i=1;i<=n/2;i++){
    
    
                cout<<x[i]<<" ";
            }
            cout<<endl;
        }
    }
    else
    {
    
    
        for(i=k;i<=n;i++){
    
    
            swap(&x[k],&x[i]);
            tempw+=x[k];
//            for(int j=1;j<=n;j++){
    
    
//                cout<<x[j]<<" ";
//            }
//            cout<<endl;
//            cout<<"tempw=="<<tempw<<endl;
            if(tempw<=totalw/2){
    
    
                backtrace(k+1);
            }
            tempw=tempw-x[k];
            swap(&x[k],&x[i]);
        }
    }
}


int main(){
    
    

    int i;
    //拔河总人数
    cin>>n;
    //初始化装入对象
    for(i=1;i<=n;i++){
    
    
        cin>>x[i];
        totalw+=x[i];
    }
    backtrace(1);
    return 0;
}

旅行商问题

#include<iostream>
using namespace std;
#define MAX 1000
int g[100][100], x[100], bestx[100];

int cl = 0, bestl = MAX, n;

void Traveling(int t) {
    
    
	int j;
	if (t > n) {
    
     //到达叶子结点
		if (g[x[n]][1] != -1 && (cl + g[x[n]][1] < bestl)) {
    
     //推销员到的最后一个城市与出发的城市之间有路径,且当前总距离比当前最优值小
			for (j = 1; j <= n; j++)
				bestx[j] = x[j];
			bestl = cl + g[x[n]][1];
		}
	} else {
    
     //没有到达叶子结点
		for (j = t; j <= n; j++) {
    
     //搜索扩展结点的左右分支,即所有与当前所在城市临近的城市
			if (g[x[t - 1]][x[j]] != -1 && (cl + g[x[t - 1]][x[j]] < bestl)) {
    
     //若果第t-1个城市与第t个城市之间有路径且可以得到更短的路线
				swap(x[t], x[j]);    //保存要去的第t个城市到x[t]中
				cl += g[x[t - 1]][x[t]]; //路线长度增加
				Traveling(t + 1);    //搜索下一个城市
				cl -= g[x[t - 1]][x[t]];
				swap(x[t], x[j]);
			}
		}
	}
}
int main() {
    
    
	int i, j;

	cin >> n;


	for (i = 1; i <= n; i++)
		for (j = 1; j <= n; j++)
			cin >> g[i][j];

	for (i = 1; i <= n; i++) {
    
    
		x[i] = i;
		bestx[i] = 0;
	}
	Traveling(2);
	cout << "城市路线:" << endl;
	for (i = 1; i <= n; i++)
		cout << bestx[i] << ' ';
	cout << bestx[1];
	cout << endl;
	cout << "最短路线长度:" << endl;
	cout << bestl << endl;
	return 0;
}

连续邮资问题

#include <stdio.h>
#include<malloc.h>
 
#define MAX_NM 10
#define MAX_POSTAGE 1024
#define INF 2147483647
 
int n, m;
int x[MAX_NM], ans[MAX_NM], y[MAX_POSTAGE],  maxStamp, r;
 
/*
 * backtrack(i)表示x[0...i-1]这i张邮票已经完全确定,
 * 相应于x[0...i-1]的最大连续邮资区间r和每种邮资所需要的
 * 最少邮票张数y[0...r]也都确定,现在枚举x[i]
 * 的每个值,确定x[i]
 */

void backtrack(int i) {
    
    
    int *backup_y, backup_r;
    int next, postage, num, tmp;
 	
    if(i >= n) {
    
    
        if(r > maxStamp) {
    
    
            maxStamp = r;
            for(tmp = 0; tmp < n; tmp++)
                ans[tmp] = x[tmp];
        }
        return;
    }
 	//临时的存储贴出某价值邮票所需的邮票数 
    backup_y = (int*)malloc(MAX_POSTAGE * sizeof(int));
    for(tmp = 0; tmp < MAX_POSTAGE; tmp++)backup_y[tmp] = y[tmp];
    //临时的最大邮资区间 
    backup_r = r;
 	//下一个票数的区间应该是大于x[i-1]并且小于等于当前的最大邮资区间+1,因为如果定义r+2作为下一个,那么r+1就会空出来 
    for(next = x[i - 1] + 1; next <= r + 1; next++) {
    
    
        /* update x[i] */
        //第i个位置的邮资 
        x[i] = next;
        /* update y */
        for(postage = 0; postage < x[i-1] * m; postage++) {
    
    
            if(y[postage] >= m)continue;
            for(num = 1; num <= m - y[postage]; num++)
                if(y[postage] + num< y[postage + num * next]	//y[postage]是不能是inf 
                   && (postage + num * next< MAX_POSTAGE))	//下标不要越界了 
                    y[postage + num * next] = y[postage] + num;
        }
        /* update r */
        while(y[r + 1] < INF) r++;
 
        backtrack(i + 1);
 
        /* restore */
        r = backup_r;
        for(tmp = 0; tmp < MAX_POSTAGE; tmp++) y[tmp] = backup_y[tmp];
    }
    free(backup_y);
}
 
int main() {
    
    
    int i;s
 
    scanf("%d%d", &n, &m);
 
    x[0] = 1;
    //r的定义为最大邮资区间,每一个都是1就是最大的范围了。 
    r = m;	
    //当前情况下贴出某邮资需要的最少邮票数,y[i]=i. 
    for(i = 0; i <= r; i++) y[i] = i;
    
    while(i < MAX_POSTAGE) y[i++] = INF;
    //记录最后的最大邮资区间 
    maxStamp= 0;
 
    backtrack(1);
 
    printf("max stamp is: %d\n", maxStamp);
    for(i = 0; i < n; i++) 
		printf("%4d", ans[i]);
 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_57663206/article/details/129269820