回溯法-(排列,子集合,TSP,n皇后,0-1背包)

目录

1.排列

任务描述

输入格式

输出格式

Sample Input

Sample Output

代码实现

2.子集合

任务描述

输入格式

输出格式

Sample Input

Sample Output

代码实现

3.TSP问题

任务描述

输入格式

输出格式

Sample Input

Sample Output

代码实现 

4.n皇后问题

任务描述

输入格式

输出格式

Sample Input

Sample Output

代码实现

5.0-1背包

任务描述

输入格式

输出格式0

Sample Input

Sample Output

代码实现 


1.排列

任务描述

1.设计算法从前m个大写字母(m≤26)种取出n个字母的所有排列(组合),并编程实现

输入格式

输入M N
1<=M=26, N<=M

输出格式

按字典序输出排列
注意:行末不输出多余空格

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<stdio.h>
int ans[26];
int flag[26];//标记该字母是否已经用过 
char a[26];
void search(int c,int n,int m){
	if(c==n){
		int i;
		for(i=0;i<n-1;i++){
			printf("%c ",a[ans[i]]);
		}
		printf("%c\n",a[ans[i]]);
	}
	else{
		for(int i=0;i<m;i++){//从m个字母中选 
			if(flag[i]) continue;//如果已被引用则跳过这次循环 
			ans[c]=i;//存放答案 
			flag[i]=1;//标记i序号的字母已被引用
			search(c+1,n,m);
			flag[i]=0; 
		}
	}
}
 int main(){
     int M,N;
     scanf("%d %d",&M,&N);
     for(int i=0;i<26;i++){
     	a[i]='A'+i;//a用来存放字母
	 }
	 search(0,N,M);
	 return 0;
 }

2.子集合

任务描述

设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法

输入格式

输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值
接下来的1行中,有n个正整数,表示集合S中的元素
n<10,c<100

输出格式

输出所有满足条件的集合,当问题无解时,输出“No Solution!”。
注意:末尾不输出多余空格
按照输入顺序输出,比如第一个满足条件的子集合是[2 2 6],那么就先输出它,子集合内的数也按照输入顺序输出

Sample Input

5 10
2 2 6 5 4

Sample Output

2 2 6
6 4

代码实现

 #include<stdio.h>
 int w[10];//存放整数
 int x[10];//存放0、1的标志
 int ws=0;//目前选择的元素的和
 int rs;//目前未选中的元素的和
 int n;int c;
 int count=0;//计算x中1的个数
 void H(int t,int ws,int rs){
     if(t>=n){//已经回溯到叶节点
        if(ws==c){//需要判断是否为可行解,是则输出
            for(int i=0;i<n;i++){
                    if(x[i]==1) count++;
                }
            for(int j=0;j<n;j++){
                if(x[j]==1){
                    if(count==1){
                        printf("%d\n",w[j]);
                    }
                    else printf("%d ",w[j]);
                    count--;
                 }
            }
        }
    }
    else{//还没有回溯到叶节点
        if(ws+w[t]<=c){//左孩子剪枝
            x[t]=1;
            H(t+1,ws+w[t],rs-w[t]);
        }
        if(ws+rs>=c){//右孩子剪枝
            x[t]=0;
            H(t+1,ws,rs-w[t]);
        }
    }
}
 int main(){
     scanf("%d %d",&n,&c);
     for(int i=0;i<n;i++){
         scanf("%d",&w[i]);
         rs+=w[i];
     }
     H(0,ws,rs);
     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<stdio.h>
 int n;
 int a[8][8];//记录距离
 int r[8];//记录路径
 int bestr[8]={0};//记录最优路径
 int bestp=999;//最少花费
 int cp=0;//当前花费
 void swap(int i,int j){//用于交换
	int temp=r[i];
	r[i]=r[j];
	r[j]=temp;
}
 void TSP(int c,int cp){
     if(c>n){
         if((a[r[n]][1]+cp<bestp)&&(a[r[n]][1])){
             bestp=cp+a[r[n]][1];
             for(int i=1;i<=n;i++){
                 bestr[i]=r[i];
             }
         }
     }
     else{
         for(int i=c;i<=n;i++){
             if((a[r[c-1]][r[i]])&&(cp+a[r[c-1]][r[i]]<bestp)){
                 swap(r[i],r[c]);
                 TSP(c+1,cp+a[r[c-1]][r[c]]);
                 swap(r[i],r[c]);
             }
         }
     }
 }
 int main(){
     scanf("%d",&n);
     for(int i=1;i<=n;i++){
         for(int j=1;j<=n;j++){
             scanf("%d",&a[i][j]);
         }
        r[i]=i;
     }
     TSP(2,cp);
     printf("%d\n",bestp);
     for(int i=1;i<=n;i++){
         printf("%d ",bestr[i]);
     }
     printf("1");
     return 0;
 }

4.n皇后问题

任务描述

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

输入格式

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

输出格式

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

Sample Input

1 8
5 0

Sample Output

1 92
10

代码实现

 #include<stdio.h>
 #include<math.h>
 int N;
 int ans[10];
 int a[10];//用来标记行是否已经有皇后
 int n;//每次回溯的深度
 int sum=0;//每次所得到的答案
 int check(int k){//检查是不是符合条件
        for(int j=1;j<k;j++){
            if((abs(k-j)==abs(a[k]-a[j]))||a[k]==a[j]){
                return 0;
            }
        }
        return 1;
 }
 void dfs(int c){
     if(c>n){
         sum++;
     }
     else{
         for(int j=1;j<=n;j++){
             a[c]=j;//第c个皇后在j列
            if(check(c)==1){
                  dfs(c+1);
            }
         }
     }
 }
 int main(){
     for(int i=1;i<=10;i++){
         n=i;
         dfs(1);
         ans[i]=sum;
         sum=0;
     }
     while(scanf("%d",&N)==1&&N!=0){
         printf("%d\n",ans[N]);
     }
 }

5.0-1背包

任务描述

输入格式

第一行输入n,c,分别代表物品的数量和背包的容量
第二行输入n个数 分别代表每个物品的重量
第三行输入n个数,分别代表每个物品的价值
1<n<100,1<c<100,重量不超过100,价值不超过1000

输出格式0

输出一个数,代表能装入的最大价值

Sample Input

4 7
3 5 2 1
9 10 7 4

Sample Output

20

代码实现 

#include<stdio.h>
int n;//物品数量
int c;//背包重量
int w[100];//物品重量
int v[100];//物品价值
int cp=0;//目前装了的总价值
int rp;//还没有装的物品的总价值
int cw=0;//目前装了的总重量
int bestp=0;//目前得到的最大的价值
void package(int i,int cp,int rp,int cw){
    if(i>=n){//到达叶子节点时
        if(cp>bestp){
            bestp=cp;
        }
    }
    else{
        if(cw+w[i]<=c){//左剪枝
            package(i+1,cp+v[i],rp-v[i],cw+w[i]);
        }
        if(cp+rp>bestp){
            package(i+1,cp,rp-v[i],cw);
        }
    }
}
int main(){
    scanf("%d %d",&n,&c);
    for(int i=0;i<n;i++){
        scanf("%d",&w[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%d",&v[i]);
        rp+=v[i];
    }
    package(0,cp,rp,cw);
    printf("%d",bestp);
    return 0;
} 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值