广东外语外贸大学算法题之回溯篇

0-1背包 (50分)

给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。
输入格式:
共有n+1行输入: 第一行为n值和c值,表示n件物品和背包容量c; 接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。
输出格式:
输出装入背包中物品的最大总价值。

输入样例:
在这里给出一组输入。例如:

5 10
2 6
2 3
6 5
5 4
4 6

输出样例:
在这里给出相应的输出。例如:

15
#include<iostream>
#include<algorithm>
using namespace std;
int n,c;	//分别定义物品种类数n,背包容量为c 
int rest;	//rest用于存储当前背包剩余物品的总价值 
int weight[150];	//定义weight数组用来存放n个物品的重量 
int value[150];		//定义value数组用来存放n个物品的价值 
int temResult[150];	//定义temResult数组,用来存放当前的选择情况,temResult[i]的值为1表示物品i被选,为0表示未选 
int result[150];	//定义一个result数组,用来存放最佳选择情况 
int totalWeight=0;	//在当前选择的情况下物品的总重量 
int temMaxNum=0,max_num=0;	//temMaxNum表示当前选择情况下的价值 ,max_num表示最佳选择情况的价值 
void Backtrack(int depth){
	if(depth>n){					//搜索到了叶节点,判断一下当前选择情况下的价格tenMaxNum和max_num的大小,如果tenMaxNum大于max_num则更新最优值 
		if(temMaxNum > max_num){
			for(int i =1;i<=n;i++)
				result[i]=temResult[i];
		max_num=temMaxNum;
		}
		return;
	}
	if(totalWeight+weight[depth] <= c){ 	// totalWeight+weight[depth]小于等于c,表示该物品能放入 
		temResult[depth]=1;				//该物品放入背包,置1 
		rest-=value[depth];				//剩余物品价值要减掉这一层的物品 
		totalWeight+=weight[depth];		//总重量要加上这一层的物品 
		temMaxNum+=value[depth];		//总价值要加上这一层的物品 
		Backtrack(depth+1);				//继续向下查找 
		rest+=value[depth];				//回溯,剩余价值rest加回这一层的value 
		temMaxNum-=value[depth];		//总价值要减去这一层的value 
		totalWeight-=weight[depth];		//总重量要减去这一层的weight 
	}
	temResult[depth]=0;
	if(rest+temMaxNum > max_num)Backtrack(depth+1);  //剪枝,只有当当前总价值加上剩余的价值大于最优值,才有可能出现更好地最优值,这时候才继续向下查找 
}
int main(){
	cin>>n>>c;
	for(int i=1;i<=n;i++){
		cin>>weight[i]>>value[i];
		rest+=value[i];
	}
	Backtrack(1);
	cout<<max_num<<endl;
} 

工作分配问题 (50分)

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。 设计一个算法,对于给定的工作费用,为每一个人都分配1 件不同的工作,并使总费用达到最小。
输入格式:
输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。
输出格式:
将计算出的最小总费用输出到屏幕。

输入样例:
在这里给出一组输入。例如:

3
10 2 3
2 3 4
3 4 5

输出样例:
在这里给出相应的输出。例如:

9

方法1:排列树法

#include<iostream>
using namespace std;
int n;		//n表示n件工作分配给n个人 
int bestx=100000,fee=0;		//bestx用于存储最优情况下的最小费用,fee表示当前选择情况下的总费用 
int c[30][30];  //二维数组c用于存放所有费用 
int x[30];		//数组x用于存放当前选择的情况 
int result[30];	//用于存放最优选择 
void Backtrace(int t){
    if(t>n){	//搜索到叶子结点,判断是否要更新最优值 
       if(fee <bestx){		//如果所得费用比最优值的都小的话,就进行更新
          bestx=fee;	
          for(int i=1;i<=n;i++)
            result[i]=x[i];
       }
        
    }else{
       for(int i=t;i<=n;i++){	//这是排列树 ,需要有x数组进行交换
        	fee += c[t][x[i]];	//t表示的是深度,也就是工作序号,x[i]表示分配给的人
        	swap(x[i],x[t]);	//排列树中经典的交换 
        	if(fee<bestx)	Backtrace(t+1); //剪枝。只有当当前总费用小于最优值时再继续查找 
        	swap(x[i],x[t]);		//回溯,把之前交换的换回来   
        	fee -= c[t][x[i]];		//回溯费用        
       }
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++)
    		cin>>c[i][j];
    }
    for(int i=1 ;i<=n;i++)		//对x数组进行顺序初始化
    	x[i] = i;
    Backtrace(1);
    cout<<bestx<<endl;
//    for(int i=1;i<=n;i++)
//        cout<<result[i]<<" ";
    return 0;
}

方法2:
#include<iostream>
using namespace std;
int n;						//有n个任务分配给n个人
int c[30][30];				//定义二位数组c用来存放费用 
int x[30]={0};				//x数组用来存放当前的分配情况,x[t]的值表示第t个任务分配给第x[t]个人 
int result[30];				//存放最优选择情况 
int minFee=100000,fee=0;	//fee表示当前分配情况下的费用,minFee存放最优情况下的费用,即最小费用 
bool place(int t){			//place用于判断第t个任务能不能分配给第x[t]个人 
	for(int i=1;i<t;i++)	//从第一个开始遍历x数组 
		if(x[t]==x[i])		//如果x[t] == x[i]表示这个人已经有任务了,不能再分配 
			return false;
	return true;
}
void Backtrack(int t){
	if(t > n){						//到达叶子结点,判断一下是否要更新最优选择情况 
		if(fee < minFee){			//如果当前费用比最小费用还小,则更新最佳值 
			minFee = fee;
			for(int i=1;i<=n;i++)
		   		result[i]=x[i];  
		return;
		}
	}
	for(int i=1;i<=n;i++){
		x[t]=i;							//把第t个任务分配给第i个人 
		fee+=c[t][i];					//总费用加上这个人所需的费用 
		if(fee < minFee && place(t))	//剪枝,只有当当前总费用比最小费用小,并且该任务可以被分配给第i个人(place函数用于判断),才继续向下查找 
			Backtrack(t+1);                                                                                                  
		fee-=c[t][i];					//回溯,更新总费用 
	}                                
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>c[i][j];
	Backtrack(1);
	cout<<minFee<<endl;
//	for(int i=1;i<=n;i++)
//		cout<<result[i]<<" "; 
//	
} 

自然数的拆分 (20分)

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。 当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
输入格式:
输入n, 1<n<20。
输出格式:
按字典序输出具体的方案。

输入样例:
在这里给出一组输入。例如:

7

输出样例:

7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
#include<iostream>
using namespace std;
int n;	//定义自然数n 
int sum=0;	//sum用于存储在当前选择下的拆分和 
int a[10000];	//定义一个数组,用来存储拆分的数 
void print(int t)	//定义一个打印函数 
{
    cout<<n<<"=";
    for(int i=1;i<=t-1;i++)
        cout<<a[i]<<"+";
    cout<<a[t]<<endl;
}
void dfs(int t)
{
    for(int i=a[t-1];i<=sum;i++) //可能很多人会搞不懂为什么从a[t-1]开始,因为后拆出来的数字一定不小于前面拆出来的数字,所以从a[t-1]开始递增,保证每次拆出来的数一定不比前面小 
    {
        if(i<n) 	//i小于n是因为我们在这里不需要把这个数拆成它本身,所以当i==n时,也就是i等于被拆分数的时候,我们不拆        
        {
            a[t]=i;	  	//i放入数组,表示再拆一个数字i出来 
            sum-=i;		//数字i被拆出来,所以要减掉 
            if(sum==0)		//如果sum==0,表示不用再拆了,刚好拆完,然后调用打印函数 
                print(t); 
            else
                dfs(t+1);	//如果sum!=0,则调用下一层,继续拆 
            sum+=i;			//回溯,把在这一层拆掉的i加回去 
        }
    }
}
int main()
{
    cin>>n;
    a[0]=1;		//因为我们从a[1]开始保存拆分数,而且后面拆分数不小于前面的,所以我们把a[0]设置为1,就能保证所有拆分数都不小于1 
    sum = n;    //初始化sum 
    dfs(1);
    return 0;
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值