回溯算法解0-1背包问题

问题描述:
给定
n
个物品和一个容量为
C
的背包,请
给出物品装入背包的方案,使得背包中物品的总价值
M
最大,并
满足:
每个物品
I
的重量为
w
i
,价值为
v
i

每个物品不可拆分,要么完整装入背包,要么不在背包里。
背包中物品的总重量不能超过容量
C

实验要求:

《回溯算法解0-1背包问题》

《回溯算法解0-1背包问题》

#include<stdio.h>
#define N 100
int v[N];//价值
int w[N];//重量
int t[N];//记录拿不拿
int temp[N];
float fw[N];//防止比例小数,转换重量用
//int record[N];//记录排序后每个数字原来位置,从0开始,对应vw数组
int C;//给出背包容量
int n;//物品个数
//int M = 0;//背包容量为C时最大价值
//int SurplusC;//剩余背包重量
int cw=0;//当前重量
int cv=0;//当前价值
int bestv;//当前最优价值
float tw[N],tv[N],wv[N];

void swap(float *x, float *y) {//交换函数
	float temp;
	temp = *x;
	*x = *y;
	*y = temp;
}

void Rank(float s[], int l, int r) {//用快速排序从大到小排序
	if (l < r) {
		//swap(&s[l], &s[(l + r) / 2]);//将中间的这个数和第一个数交换,可提高效率 
		int i = l, j = r;//左边从第一个数开始,右边从最后一个数开始
		float x = s[l];
		while (i < j) {
			while (i < j && s[j] <= x) // 从右向左找第一个大于x的数  
				j--;
			if (i < j) {
				s[i] = s[j];
				swap(&tw[i], &tw[j]);//交换重量
				swap(&tv[i], &tv[j]);//交换价值
				i++;
			}

			while (i < j && s[i] > x) // 从左向右找第一个小于等于x的数  
				i++;
			if (i < j) {
				s[j] = s[i];
				swap(&tw[j], &tw[i]);//交换重量
				swap(&tw[j], &tw[i]);//交换价值
				j--;
			}
		}
		s[i] = x;//实现交换		
		Rank(s, l, i - 1); // 递归调用   
		Rank(s, i + 1, r);
	}
}

int bound(int i) {//计算上界
	int j;
	int cleft = C - cw;//剩余容量
	int bound = cv;
	for(j=0;j<n;j++){
		tw[j]=w[j];
		tv[j]=v[j];
		wv[j]=v[j]/w[j];
	}
	Rank(wv,i,n-1);
	//以物品单位重量价值递减序装入物品,w[i]已排好序
	while (i < n && w[i] <= cleft) {
		cleft -= w[i];
		bound += v[i];
		i++;
	}
	//装满背包
	if (i < n)
		bound += v[i] / w[i] * cleft;
	return bound;
}

void backtrack(int i) {//回溯算法
	//搜索第i层结点
	int j;
	if (i >= n) {//到达叶结点
		if(cv>bestv){
			bestv = cv;
			for(j=0;j<n;j++)
				t[j]=temp[j];
		}
		return;
	}
	//搜索子树
	if (cw + w[i] <= C) {//进入左子树
		cw += w[i];
		cv += v[i];
		temp[i] = 1;
		backtrack(i + 1);
		cw -= w[i];
		cv -= v[i];
	}
	if (bound(i + 1) > bestv) {//进入右子树
		temp[i] = 0;
		backtrack(i + 1);
	}
}

void IO() {
	int i, k = 0, num[20];
	FILE *ifp, *ofp;
	ifp = fopen("input.txt", "r");
	fscanf(ifp, "%d\r\n", &C);// /r/n实现记事本换行/r回车符/n换行
	while (!feof(ifp)) {
		fscanf(ifp, "%d\t%d\t%d\r\n", &num, &w[k], &v[k]);
		k++;
	}
	fclose(ifp);
	n = k;//单独给n赋值
	printf("文件数据已成功读取...\n");

	//for (i = 0; i<n; i++)//初始化记录数组,不知道为什么放在主函数就不执行了
	//	record[i] = i;
	//for(i=0;i<n;i++)
	//	printf("%d  ",record[i]);
	//printf("\n");

	backtrack(0);

	ofp = fopen("output.txt", "wb");
	fprintf(ofp, "%d\r\n", bestv);
	for (i = 0; i<n; i++)
	{
		fprintf(ofp, "%d\t%d\t%d\r\n", (i+1), t[i], v[i]);
	}
	fclose(ofp);
	printf("结果已输出到本目录文件\"output.txt\"中...\n");
}

int main() {
	IO();
	return 0;
}

插入图片应该不利于检索推广吧,不过谁叫自己懒呢。

在和老师沟通后得知企业普遍用传递参数而不是用全局变量,自己指针理解程度不高没敢用传递参数,所以写成了这个样子,还是姚改进的,也挺感谢算法胡老师的,是真的厉害,没有她,或许也就没有了这个算法的系列了,一共五篇,应该还有最后一篇这个学期就结束了,也因为这个让自己不畏惧算法,不讨厌算法。自己的大多数算法都没有看课本给的代码,一来是看不懂,二来也是自己上课听了,知道原理是有写的逻辑的,虽然可能写的不好,但至少可以完成罢了。每次听室友说这个多难多难,他请教班里学霸也用了一天就感到很诧异,不过对于写这个系列博客的初衷而言,结果是一样的,毕竟自己学了就忘,记忆力实在差的可以,只能以这样的方式表示自己做过,并且帮助一下后来人,如果有人自己写不出来,看看自己代码有了启发也是极好的。

当然还是不要交差型的抄了,自己不喜欢问班里的同学,也不想去承认不如他们,所以深切体会到这些默默野蛮生长的天涯同路人的艰辛,最后还是愿自己这些微小的工作可以帮到你们,如有错误请多多指教,阿里嘎到~

    原文作者:回溯法
    原文地址: https://blog.csdn.net/b296405422/article/details/80632226
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞