01背包问题 及c++ 代码实现

    今天在看july的博客之时,看到其中一道题目的原理为01背包问题,就自己温习了下,写下今天的学习体会。

本文理论分析参考博客:http://www.cnblogs.com/qinyg/archive/2012/04/26/2471829.html

问题描述:   

   

    给定N个物品和一个背包,其中物品i的重量是Wi,其价值为Vi ,背包的容量为C。问应该如何选择装入背包的物品,使得转入背包的物品的总价值为最大?

   (关于01背包问题的其他变种请参看

http://zh.wikipedia.org/wiki/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98

http://love-oriented.com/pack/

    )

     对于此类01背包问题,我们可以使用动态规划分析解决:

    问题分析:

    在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能讲物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。

    令V(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为就j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:

(1)   V(i,0)=V(0,j)=0 

(2)   V(i,j)=V(i-1,j)  j<wi  

(3)      V(i,j)=max{V(i-1,j) ,V(i-1,j-wi)+vi) } j>wi

(2)表明:如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;

(3)表明:如果第i个物品的重量小于背包的容量,则会有一下两种情况:(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi;(b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解。

(注:一开始大家对第三个式子比较难以理解,我们可以从这么几点帮助理解:

1.这前i个中,并不是所有的i都被选择了,请大家看下面的代码会更加加深理解。

2.动态规划的概念,有最有子结构,重复子问题。V(i,j)其实是有记录作用的。

我们来看下代码实现:

#include<iostream>
#include<cstdlib>

using namespace std;

int maxNum(int a,int b)
{
    return (a>b)?a:b;
}

void insight(int *exist,int **val,int *w,int bagnum,int weight)
{
    for(int i=bagnum;i>0;i--)
    {   
        if(val[i][weight]>val[i-1][weight])
        {   
            exist[i]=1;
            weight=weight-w[i];
        }   
    }   
    
    for(int j=1;j<=bagnum;j++)
        cout<<exist[j]<<" ";
}

int maxVal(int **val,int *v,int *w,int bagnum,int weight)
{
    int i,j;
    for(i=0;i<=bagnum;i++)
        val[i][0]=0;
    for(i=0;i<=weight;i++)
        val[0][i]=0;
    
    for(i=1;i<=bagnum;i++)
    {   
        for(j=1;j<=weight;j++)
        {   
            if(j<w[i])
                val[i][j]=val[i-1][j];
            else
 val[i][j]=max(val[i-1][j],val[i-1][j-w[i]]+v[i]);
        }
    }

    for(i=0;i<=bagnum;i++)
        for(j=0;j<=weight;j++)
        {
            cout<<val[i][j]<<" ";
            if(j==weight)
                cout<<endl;
        }

    return val[bagnum][weight];

}


int main()
{
    int bagnum,weight;
    int i,j;
    cout<<"please input the number of bags:"<<endl;
    cin>>bagnum;
    cout<<"please input the limit weight of bag:"<<endl;
    cin>>weight;

    int **val=new int *[bagnum+1];                 //这样定义二维数组,是为了方便传入
    val[0]=new int [(bagnum+1)*(weight+1)];
    for(i=1;i<=bagnum;i++)
        val[i]=val[i-1]+weight+1;

    cout<<"please input the weight of every bag:"<<endl;
    int *w=new int[bagnum+1]();
    for(i=1;i<=bagnum;i++)
        cin>>w[i];
    //cout<<"w=";
    //for(i=0;i<=bagnum;i++)
    //    cout<<w[i]<<" ";
    //cout<<endl;

    cout<<"please input the value of every bag:"<<endl;
    int *v=new int[bagnum+1]();
    for(i=1;i<=bagnum;i++)
        cin>>v[i];
    //cout<<"v=";
    //for(i=0;i<=bagnum;i++)
    //    cout<<v[i]<<" ";
    //cout<<endl;

    cout<<maxVal(val,v,w,bagnum,weight)<<endl;

    int *exist=new int[bagnum]();           //为了查看我们选择了哪些包
    cout<<"we get the bag num is:";
    insight(exist,val,w,bagnum,weight);
    cout<<endl;

    // int **val=(int **)malloc((bagnum+1)*sizeof(int *));
    // for(i=0;i<bagnum+1;i++)
    //     val[i]=(int *)malloc((weight+1)*sizeof(int));
    // 

    return 0;
}

看完此处之后,我们在来看下july大神写的关于


请看http://blog.csdn.net/v_july_v/article/details/6419466

第二节、寻找和为定值的多个数
第21题(数组)
2010年中兴面试题
编程求解:
输入两个整数 n 和 m,从数列1,2,3.......n 中 随意取几个数,
使其和等于 m ,要求将其中所有的可能组合列出来

并附上我的代码,较原文给出的会更加容易理解一点

#include<iostream>
#include<list>
using namespace std;

list<int> lis;                 
 //这边我们将list用作了栈,当然也可以用stack容器,不过stack其实是一种容器适配器,其默认实现方式是用list实现的
void findNSum(int sum,int n)
{
    if(sum<=0||n<=0)             //递归退出条件
        return;
    if(sum==n)                   //打印条件
    {   
        for(list<int>::iterator iter=lis.begin();iter!=lis.end();iter++)
            cout<<*iter<<"+";
        cout<<n<<endl;                        //这是由于,此时的n并没有被压在原队列中
    }   
    lis.push_back(n);                        //将n压入list中
    findNSum(sum-n,n-1);                     //n装入以后,刚好等于sum
    lis.pop_back();                          //将刚才压入list中的数据弹出
    findNSum(sum,n-1);                       //没有选择n,说明前面n-1个数已经就可以满足条件
}


int main()
{
    int sum,n;
    cout<<"please input the sum:"<<endl;
    cin>>sum;
    cout<<"please input the n:"<<endl;
    cin>>n;
    findNSum(sum,n); 
    return 0;
}




你可能感兴趣的:(C++,算法)