【算法设计与分析】回溯法(排序、子集和、TSP、n皇后)

第1关:排列

在这里插入图片描述
Sample Input

4  2  

Sample Output

A B
A C
A D
B A
B C
B D
C A
C B
C D
D A
D B
D C 

分析:
注意 是所有情况的枚举
解题代码:

//#include<bits/stdc++.h>
#include<stdio.h>
//using namespace std;
int m,n;
int an[10010],a[10010],ck[10010];
void cal(int x){
    
    
	if(x==n){
    
    
		for(int i = 0;i<n;i++){
    
    
           printf("%c",an[i]);  if(i!=n-1) printf(" ");
        } 
		printf("\n");
		return;
	}else{
    
    
		for(int i = 0 ; i < m;i++){
    
    
			if(ck[i]!=1){
    
    
				an[x] = a[i];
				ck[i]	= 1;
				cal(x+1);
				ck[i] = 0;
			} 
		}	
	}
}
int main(){
    
    
	scanf("%d%d",&m,&n);
	for(int i = 0; i <m;i++){
    
    
		a[i] = i+ 'A';
		ck[i] = 0;
	}
	cal(0);
	return 0;
}

第2关: 子集合

在这里插入图片描述
Sample Input

5 10
2 2 6 5 4  

Sample Output

2 2 6
6 4 

思路: 就是一个子集树 找到符合条件的子集的限定条件就可以了

解题代码:

#include<stdio.h>
//#include<bits/stdc++.h>
//using namespace std;
int a[20],an[20], ck[20];
int n,c; 
int f = 0;
void solve(int x,int sum,int st){
    
    
	if(sum>c) return; //剪枝
	if(sum==c){
    
    
		f = 1;//标记是不是  存在解
		for(int i = 0; i <x;i++){
    
      //输出
            printf("%d",an[i]); if(i!=x-1) printf(" ");
        } printf("\n");	
		return;
	}else{
    
    
		for(int i = st; i <n;i++){
    
      //解空间树结点下的结点  是有范围的
			if(ck[i]!=1){
    
    
				an[x] = a[i]; ck[i] = 1;
				//cout<<"i: "<<i<<" "<<a[i]<<" "<<sum<<" "<<ck[i]<<endl;
				solve(x+1,sum+a[i],i);
				ck[i] = 0;
			}
		}
	}
}
int main(){
    
    
	scanf("%d%d",&n,&c);
	for(int i = 0; i < n;i++) scanf("%d",&a[i]); 
	solve(0,0,0);
	if(!f) printf("No Solution!"); 
	return 0;
} 

第3关:TSP问题

在这里插入图片描述
输入格式
第一行输入n,代表有n个城市。
接下来n行每行输入n个数,第i行j列的值代表i市到j市的距离,0代表城市之间不通
注意:起点和终点都为1
n<7,城市之间的距离都不超过100
输出格式
第一行输出最少的旅行费用
第二行输入旅行路径
(保证只有一条最短旅行路径)

Sample Input

4  
0 30 6 4
30 0 5 10
6 5 0 20
4 10 20 0  

Sample Output

25
1 3 2 4 1

代码一:

// #include<bits/stdc++.h>
// using namespace std;
#include<stdio.h>
int a[110][110];
int n;
int f[110],p[110],pd[110];
int minn = 1000010,sum = 0,len = 1;
void solve(int x){
    
    
    if(len==n&&a[x][1]!=0){
    
     //就是 最后要回到起点
        if(sum+a[x][1]<minn){
    
    
            for(int i = 0; i<n;i++){
    
    
                pd[i] = p[i];  //存一下  不断更新  找最小的
            }
            minn = sum +a[x][1];
        }
    }
    if(sum>minn) return ;
    if(len<n){
    
    
        for(int j = 1; j <= n;j++){
    
    
            if(f[j]==0&&a[x][j]!= 0){
    
    
                sum += a[x][j]; p[len] = j;
                len++;  f[j] = 1;
                
                solve(j);
                f[j] = 0;
                len--;
                sum -= a[x][j];
            }
        }
    }
}
int main(){
    
    
    scanf("%d",&n);
    for(int i = 1; i <= n;i++){
    
    
        for(int j = 1; j <= n;j++){
    
    
             scanf("%d",&a[i][j]);
        }
    }
    f[1] = 1;  solve(1);
    printf("%d\n",minn);
    printf("1 ");
    for(int i = 1; i < n;i++)  printf("%d ",pd[i]);
    printf("1");
    return 0;
}

代码二:

 #include <stdio.h>
#define MAX 100 

int n;
int b[8]={
    
    1,2,3,4,5,6,7};
int a[MAX][MAX],x[8];
int mindis=1000;

void swap(int &a,int &b)
{
    
    
	int t=a;
	a=b;
	b=t;
}

void dfs(int i,int dis)
{
    
    
	if(dis>=mindis)
		return;
	else if(i==n)
	{
    
    
		if(dis+a[b[i-1]][1]<mindis)
		{
    
    
			mindis=dis+a[b[i-1]][1];
			for(int j=1;j<n;j++)
				x[j]=b[j];
		}
		return;
	}
	else
	{
    
    
		for(int j=i;j<n;j++)
		{
    
    
			swap(b[i],b[j]);
			dfs(i+1,dis+a[b[i-1]][b[i]]);
			swap(b[i],b[j]);
		}
	}
}
int main() {
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    
    
    	for(int j=1;j<=n;j++)
    		scanf("%d",&a[i][j]);
	}
	
	dfs(1,0);
	printf("%d\n",mindis);
	printf("1");
	for(int i=1;i<n;i++)
		printf(" %d",x[i]);
	printf(" 1");
    return 0;
}

第4关:n皇后问题

任务描述
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
输入格式
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
输出格式
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

任务描述
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

输入格式
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。

输出格式
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

Sample Input

1  
8
5  
0

Sample Output

1  
92
10

思路:
其实解空间树的限制条件 ,就是在这条解的集合上,判断当前节点的所在行、斜线和反斜线上是不是已经有皇后了。

解题代码:

//#include<bits/stdc++.h>
#include<stdio.h>
//using namespace std;
const int N = 20;
int n;
int col[N],dg[N],udg[N];
int ans;
void solve(int u){
    
    
     if(u==n){
    
    
         ans++; return;
     }else{
    
    
         for(int i = 0; i< n;i++){
    
    
                 if(!col[i]&&!dg[u+i]&&!udg[u+n-i]){
    
    
                  col[i] = dg[u+i] = udg[u+n-i] = true;
                  solve(u+1);
                  col[i] = dg[u+i] = udg[u+n-i] =  false;
              }
         }
          
     }
}
int main(){
    
    
     while(~scanf("%d",&n)&&n){
    
    
         ans = 0;
         solve(0);
        printf("%d\n",ans); 
     }
    return 0;    
} 

第5关:0-1背包

**思路:**就是拿动态规划过的 没啥好说的

解题代码:

#include<stdio.h>
// #include<bits/stdc++.h>
// using namespace std;
int n,c,w[110],v[110];
int f[110][110]; 
int main(){
    
    
    scanf("%d%d",&n,&c);
    for(int i = 1; i <= n;i++){
    
    
            scanf("%d",&w[i]);
    }
    for(int i = 1; i <= n;i++){
    
    
            scanf("%d",&v[i]);
    }
    for(int i = 1; i <= n;i++){
    
    
            for(int j = 0; j <= c;j++){
    
    
                    f[i][j] = f[i-1][j];
                    if(j>=w[i]&&(f[i-1][j-w[i]]+v[i] > f[i][j])) f[i][j] = f[i-1][j-w[i]]+v[i] ;
            }             
    }
    printf("%d",f[n][c]);
    return 0;    
} 

猜你喜欢

转载自blog.csdn.net/qq_52626583/article/details/124818199