HNU_算法_实验2(2021级)-经典案例1-动规求解 0-1 背包

   一、实验名称

用动态规划法求解 0-1 背包问题;

(1)问题描述:动态规划法求解 0-1 背包问题

(2)给定任意几组数据,利用动态规划法求解 0-1 背包问题的思想,选好物

品使得背包价值最大。

二、实验目的

        通过上机实验,要求掌握动态规划算法的问题描述、算法设计思想、程序设

计。

三、实验原理

利用动态规划法求解 0-1 背包问题,并计算出程序运行所需要的时间。

四、实验步骤

        给定一个背包,容量为c, 给定n件物品{1,2……n},第 i 件物品的体积为 wi , 价值为 vi, 现在求一种方案,将价值尽可能大的物品装入背包。

        ①求最优解,易证本题的最优子结构性质为:

        t ( i, j ) = max{ vi + t (i-1, j-wi),t(i-1, j) }

        t(i,j) 表示前 i 件物品放入容量为 j 的背包的最大价值。

证明:(反证法)

        假设规模为n的最优解S,它包含的子问题E的解不是n-1规模中最优的:

        即存在E'>E,那么S=E+vn(如果选取了n的话)<E'+vn 因此S不是原问题的最优解,矛盾。

        ②解决问题的策略:利用动态规划的思想,解决问题的规模从小到大,自底向上,利用状态转移方程得到原问题的最优解。

五、关键代码

1.动规求解:


int fun(int n,int m,vector<int> w,vector<int> v,vector<vector<int> >& f)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(j<w[i])
				f[i][j]=f[i-1][j];
			else{
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
//				cout<<"i="<<i<<"\tj="<<j<<endl;
//				cout<<"f[i-1][j]="<<f[i-1][j]<<"\tf[i-1][j-w[i]]+v[i]="<<f[i-1][j-w[i]]+v[i]<<"\tf[i][j]="<<f[i][j]<<endl<<endl;
			}				
		}
	}
		return f[n][m];
}

2.生成不同规模样例: 

	cout<<"请输入数据规模n:"<<endl; 
		int n,c;
		cin>>n;
		ofstream out("input1.txt");
		out<<n<<'\n';
		srand((unsigned)time(NULL));
		// c
		int a=0,b=100;//因为要建成的数组为f[n+1][c+1],所以c如果不做限制的话,这个数组会很大,一开始没做限制时,即使我设置和输入规模n为1,也会运行很长时间。
		out<< (rand() % (b-a))+ a + 1<<'\n';
		// v数组 
		for(int i=0;i<n;i++){
			out<<rand()<<' ';
			if((i+1)%10==0) out<<'\n';
		}
		out<<'\n';
		// w数组 
		for(int i=0;i<n;i++){
             //相应于上面的受限制的c,在这里对w也做相同限制。
			out<< (rand() % (b-a))+ a + 1<<' ';
			if((i+1)%10==0) out<<'\n';
		}
		out.close();

3.对实参的部分初始化:

        vector<int> w(n+1),v(n+1);
		v[0]=0;//对v,w的第0个元素赋值为0
		w[0]=0;
		vector<vector<int> > f(n+1, vector<int>(c+1));
		for(int i=0;i<=n;i++) f[i][0]=0;//对f的第0列、第0行赋值0
		for(int j=0;j<=c;j++) f[0][j]=0;

 

六、测试结果

时间复杂度:O(nc)

利用动态规划解决此问题的效率即是填写此张表的效率,为O(nc),

用到二维数组存储子问题的解,所以动态规划的空间效率为O(n*c);

 

七、实验心得

        通过这次实验,我更为掌握了动态规划算法,并且自己生成案例并测试运行时间的过程中,熟悉了随机化算法和运行时间的计算。

        实验可改进的地方:随机化过程的加入可能是的不同规模的测试数据与理论值有偏差,可以通过每个输入规模多次测试来减小误差。

        

八、完整代码

#include <iostream>
#include <fstream>
#include <windows.h>
#include <time.h>
#include <vector>
using namespace std;

int fun(int n,int m,vector<int> w,vector<int> v,vector<vector<int> >& f)
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(j<w[i])
				f[i][j]=f[i-1][j];
			else{
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
			}				
		}
	}
		return f[n][m];
}
 
void traceback(int n,int c,vector<int> w,vector<vector<int> >& f,vector<int> &x)  
{  
    for(int i=n;i>1;i--)  
    {  
        if(f[i][c]==f[i-1][c])  
            x[i]=0;  
        else  
        {  
            x[i]=1;  
            c-=w[i];  
        }  
    }  
    x[1]=(f[1][c]>0)?1:0;  
}  

int main(){
	ofstream out1("output.txt");
	while(1){
		//生成规模为n的随机数 
		cout<<"请输入数据规模n:"<<endl; 
		int n,c;
		cin>>n;
		ofstream out("input1.txt");
		out<<n<<'\n';
		srand((unsigned)time(NULL));
		// c (a,b]
		int a=0,b=100;
		out<< (rand() % (b-a))+ a + 1<<'\n';
		// v数组 
		for(int i=0;i<n;i++){
			out<<rand()<<' ';
			if((i+1)%10==0) out<<'\n';
		}
		out<<'\n';
		// w数组 (a,b]
		for(int i=0;i<n;i++){
			out<< (rand() % (b-a))+ a + 1<<' ';
			if((i+1)%10==0) out<<'\n';
		}
		out.close();
	
		int i,maxi,mini;
		LARGE_INTEGER nFreq,nBegin,nEnd;
		double time; 
		
		ifstream in("input1.txt");
		
		in>>n>>c;
//		cout<<"n= "<<n<<" ;c= "<<c<<endl; 
		vector<int> w(n+1),v(n+1);
		for(i=1;i<=n;i++){
			in>>v[i];
//			cout<<v[i]<<" ";
		}
//		cout<<endl;	
		for(i=1;i<=n;i++){
			in>>w[i];
//			cout<<w[i]<<" ";
		}
//		cout<<endl;	
		
		v[0]=0;
		w[0]=0;
		vector<vector<int> > f(n+1, vector<int>(c+1));
		for(int i=0;i<=n;i++) f[i][0]=0;
		for(int j=0;j<=c;j++) f[0][j]=0;
//		for(int i=1;i<=n;i++){
//		for(int j=1;j<=c;j++){
//			cout<<f[i][j]<<" ";
//			}
//			cout<<endl;
//		}
//		cout<<endl;
		QueryPerformanceFrequency(&nFreq);	
		QueryPerformanceCounter(&nBegin);
		int result=fun(n,c,w,v,f);
//		cout<<"物品选择情况:"<<endl;
//		vector<int> x(n+1);
//		traceback(n,m,w,f,x);  	
//    	for(int i=1;i<=n;i++)  
//       		cout<<x[i]<<"\t"; 
		QueryPerformanceCounter(&nEnd);
		time=(double)(nEnd.QuadPart-nBegin.QuadPart)/(double)nFreq.QuadPart; 
		
	//	cout<<"结果:"<<result<<"\n查询时间:"<<time<<endl<<endl;
		out1<<n<<' '<<time<<endl;
		in.close();		
	}
	out1.close();
	return 0;
}

九、绘图代码

import matplotlib.pyplot as plt

# 读取txt文件,假设文件名为data.txt
file_path = 'F:\\3-CourseMaterials\\3-1\\3-算法设计与分析\实验\lab2\\1-code\\1-动规求解0-1背包问题\\output.txt'

# 存储x和y的列表
x_values = []
y_values = []

# 读取文件并提取数据
with open(file_path, 'r') as file:
    for line in file:
        # 假设数据以空格或逗号分隔
        x, y = map(float, line.strip().split())
        x_values.append(x)
        y_values.append(y)

print(x_values)
print(y_values)
# 绘制图形
plt.plot(x_values, y_values, marker='o', linestyle='-')
plt.title('X vs Y Plot')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.grid(True)
plt.show()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值