zoukankan      html  css  js  c++  java
  • 背包问题---递归及动态规划

    一、原题

    如果有一组物品,各个物品的质量已知,现有一个背包,背包可以容纳的质量总和S已知,问是否能从这N个物品中取出若干个恰好装入这个背包中。

    二、递归算法

    本质思想:设法尝试全部组合,当部分组合已经无法满足条件时,马上停止当前组合的尝试;若出现第一个满足条件的组合,马上停止尝试。使用递归回溯法实现。(感觉这东西不是我这样的菜鸟可以说明确的,还得自己慢慢体会,最好的方法就是耐住性子跟踪调试)。

    上“酸菜”

    #include <stdio.h>
    #include <stdlib.h>
    
    #define N 7						//物品种类
    #define S 15					//背包容量
    int w[N+1]={0,1,4,3,4,5,2,7};	//各种物品的质量
    
    bool knap(int s,int n)			//s代表背包剩余容量,n代表还未尝试装载的物品种类
    {
    	if(s==0)					//恰好装完
    	{
    		return true;
    	}
    	if(s<0 || (s>0 && n<1))		//不能完毕装载
    	{
    		return false;
    	}
    	
    	if(knap(s-w[n],n-1))		//当前物品可以装载,则递归
    	{
    		printf("%d	",w[n]);
    		return true;
    	}
    	else
    	{
    		return knap(s,n-1);		//当前物品不能装载,取下一物品进行递归
    	}
    }
    
    int main(void)
    {
    	if(knap(S,N))
    	{
    		printf("OK!!!
    ");
    	}
    	else
    	{
    		printf("NO!!!
    ");
    	}
    	
    	system("pause");
    	return 0;
    }


    上面的代码只输出了第一个满足条件的组合,那么如何输出所有的有效组合呢?

    #include <stdio.h>
    #include <stdlib.h>
    
    #define N 7
    #define S 15
    int w[N+1]={0,1,4,3,4,5,2,7};
    
    //
    int save[N+1]={0};			//标记数组
    
    int knap(int s,int n)
    {
    	if(s==0)
    	{
    		return 1;
    	}
    	if(s<0 || (s>0 && n<1))
    	{
    		return 0;
    	}
    
    	if(knap(s-w[n],n-1))
    	{
    		//printf("%4d",w[n]);
    		//
    		save[n]=1;
    		return 1;
    	}
    	return knap(s,n-1);
    }
    
    void showSet(void)
    {
    	for(int i=N;i>0;i--)
    	{
    		if(save[i]==1)
    		{
    			printf("%4d",w[i]);
    		}
    	}
    }
    
    int main(void)
    {
    	bool flag;
    	for(int i=N;i>0;i--)					//不断降低物品的种类,以便遍历全部组合
    	{
    		//
    		for(int m=0;m<N+1;m++)
    		{
    			save[m]=0;
    		}
    
    
    		if(knap(S,i))
    		{
    			showSet();
    			printf("
    OK!
    ");
    			//
    			flag=true;								//当物品种类为i时,存在有效组合
    		}
    		else
    		{
    			//printf("
    NO!
    ");
    			flag=false;
    		}
    		
    		//
    		while(flag)
    		{
    			int j=N;
    			int cnt=0;
    			int s_index=0;
    			while(j!=0 && cnt!=2)
    			{
    				if(save[j]==1)
    				{
    					cnt++;
    				}
    				if(cnt==1)
    				{
    					s_index=j;
    				}
    				j--;
    			}
    			if(cnt==2)
    			{
    				for(int k=0;k<s_index;k++)
    				{
    					save[k]=0;
    				}
    
    				if(knap(S-w[s_index],j))				//从有效组合第二个物品的下一个物品開始寻找
    				{
    					showSet();
    					printf("
    OK!
    ");
    				}
    				else
    				{
    					flag=false;
    				}
    				
    			}
    			else
    			{
    				flag=false;
    			}
    		}
    
    	}
    
    	system("pause");
    	return 0;
    }

    举例来讲,因为第一个有效的组合是7,2,5,1,所下面一次从2的下一个数開始搜索,背包的容量变成8=15-7.

    三、动态规划方法

    详见參考1,这里可以将物品的质量作为它的价值,那么假设最大价值dp[N][S]等于S,则说明背包可以恰好装满。

    #include <iostream>
    using namespace std;
    #define N 7
    #define S 15
    #define max(a,b) a>b?a:b
    
    int w[N+1]={0,1,4,3,4,5,2,7};	//各种物品的质量
    
    int main(void)
    {
    	int i,j;
    	int dp[N+1][S+1];
    
    	memset(dp,0,sizeof(dp));
    	for(i=1;i<=N;i++)
    	{
    		for(j=0;j<S+1;j++)
    		{
    			if(j>=w[i])
    			{
    				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+w[i]);		//转移方程,当中w[i]能够看做各物品的价值
    			}
    			else
    			{
    				dp[i][j]=dp[i-1][j];
    			}
    		}
    	}
    
    	printf("%d
    ",dp[N][S]);
    	if(dp[N][S]==S)
    	{
    		printf("OK!!!
    ");
    	}
    	else
    	{
    		printf("NO!!!
    ");
    	}
    
    	system("pause");
    	return 0;
    }

    四、其他背包相关问题

    依据以上分析,加以变通就可以。

     

  • 相关阅读:
    开发一个delphi写的桌面图标管理代码
    web颜色转换为delphi
    delphi RGB与TColor的转换
    用Delphi制作仿每行带按钮的列表
    Delphi 之 编辑框控件(TEdit)
    numEdit
    DropDownList添加客户端下拉事件操作
    19个必须知道的Visual Studio快捷键
    asp.net线程批量导入数据时通过ajax获取执行状态
    详解JQuery Ajax 在asp.net中使用总结
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3756996.html
Copyright © 2011-2022 走看看