01背包的递归转循环

引进题目

        超市零元购,你有一个背包,你可以把超市里的东西放入背包带走,但是背包的容积有限,给出每个超市物品的价值以及它的体积,求最大价值是多少

        很显然,这是一个最基础的 01背包 模型,常规做法使用 动态转移方程 解决,这方法固然很好,但是想,正常的思维是怎么想到用这种方法? 对于我来说,我是不可能自己想到的(当然不包括个别大佬-·-·-)

        所以我们尝试用其它的较为简单的方法求解

        这里分享两种,这两种方法在题解中我是没有见到过的,是自己探究出的

第一种  递归算法

        递归的优点是可以简洁明了(就是大家都可以理解的),缺点也显而易见吗,会超时!!!

这种方法只适用于小的数据,大数据要算到猴年马月、、、

#include <stdio.h>
#include <stdlib.h>
    int n,w,ww[101],vv[101],ans;
int putin()
{
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;i++) scanf("%d",&ww[i]);
    for(int i=1;i<=n;i++) scanf("%d",&vv[i]);
}
int cy(int x,int o)
{
    if(x>n || o<=0) return 0;
    else 
    {
        if(o-ww[x]>=0)
        {
            if(cy(x+1,o-ww[x])+vv[x]>cy(x+1,o)) return cy(x+1,o-ww[x])+vv[x];
            else return cy(x+1,o);
        }
        else return cy(x+1,o);
    }
}
int putout()
{
    printf("%d",ans);
}
int main()
{
    freopen("D:/text1.in","r",stdin);
    putin();
    ans=cy(1,w);
    putout();
}

稍微看一看就知道是怎么个事

重头在第二种 

我们知道,对于一个物品,只有两种情况,一种是放,一种是不放,于是我们可以枚举所有的方法,例如有三个物品abc,我们可以枚举以下情况

Y表示放,N表示不放

方案/物品abc
1YYY
2YYN
3YNY
4YNN
5NYY
6NNY
7NYN
8NNN

可以知道,当有n个物品时,共有2的n次方个方案;

于是,我们就可以利用两个二维数组来分别储存总剩余容积和总已有价值,具体代码如下

#include <stdio.h>
#include <stdlib.h>
    int n,w[10],v[10],ww[1001][1001],vv[1001][1001]={0},W,t=-1,p,l[1];
int input()
{
    scanf("%d%d",&n,&W);
    ww[0][0]=W;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
}
int cf(int kk)
{
    int y=1;
//    printf("kk=%d  ",kk);
    for(int i=1;i<=kk;i++)
    {
        y=y*2;    
    }
//    printf("y=%d  \n",y);
    return y;
}
int zx()
{
        for(int i=1;i<=n;i++)
        {
            p=cf(i);
            for(int j=1;j<=p;j++)
            {
                if(ww[i-1][j]>=w[i] && j%2==1)
                {
                    vv[i][j]=vv[i-1][(j+1)/2]+v[i];
                    ww[i][j]=ww[i-1][(j+1)/2]-w[i];
                }
                else
                {
                    vv[i][j]=vv[i-1][j/2];
                    ww[i][j]=ww[i-1][j/2];
                }
            }
        }
}
int output()
{
    int oi;
    for(int i=1;i<=p;i++) if(vv[n][i]>t) t=vv[n][i];
    printf("result=%d\n",t);
}
int main()
{
   // freopen("D:/text1.in","r",stdin);
    input();
    zx();
    output();
} 

该程序难点就在于找到正确的数组的位置,例如下列数据:

6 100
40 38 38 36 27 45
3868 1252 1536 634 3016 4555

在函数 zx 中数组的表示就较为复杂

不难发现,二维数组的空间浪费太多太多了,于是我们想办法使用一维数组实现

将后面的数据覆盖至前面,可以看出,用从后往前的数据赋值更方便于我们的空间利用

#include <stdio.h>
#include <stdlib.h>
	int n,W,w[101],v[101],ww[101]={0},vv[101]={0},p;
int input()
{
	scanf("%d%d",&n,&W);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<=n;i++) scanf("%d",&v[i]);
	ww[1]=W;
}
int cif(int x)
{
	int y=1;
	for(int i=1;i<=x;i++) y*=2;
	return y;
}
int zhuangx()
{
	for(int i=1;i<=n;i++)
	{
		p=cif(i);
		for(int j=p;j>=1;j--)
		{
			if(j%2==0)
			{
				vv[j]=vv[j/2];
				ww[j]=ww[j/2];
			}
			else
			{
				if(ww[(j+1)/2]>=w[i])
				{
					vv[j]=vv[(j+1)/2]+v[i];
					ww[j]=ww[(j+1)/2]-w[i];
				}
				else
				{
					vv[j]=vv[(j+1)/2];
					ww[j]=ww[(j+1)/2];
				}
			}
		}
	}
}
int output()
{
	int t=-1;
	for(int i=1;i<=p;i++) if(vv[i]>t) t=vv[i];
	printf("result=%d\n",t);
}
int main()
{

	freopen("D:/text1.in","r",stdin);
	input();
	zhuangx();
	output();
}
  • 35
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值