ZOJ 3631超大背包问题(DFS || 折半搜索)

原创 2015年11月20日 18:22:37

Watashi's BG

Time Limit: 3 Seconds      Memory Limit: 65536 KB

Watashi is the couch of ZJU-ICPC Team and he is very kind hearted. In ZJU-ICPC summer training camp, students are divided into several groups and each day one of the groups will design some problems to hold a contest. Today students of Group C are required to design the problems, and they spent the whole night to check to test data which made them very tired. Watashi decides to give some money as a reward to group C so that they can buy the lunch for free.

There are N days in the training schedule, and all students have booked their lunch for N days so we know how much money they will spend in each day. Now the leader of group C needs to decide how to use Watashi's money. Since the money is limited, it may not be possible that they can have free lunch every day. So each day the leader can choose to pay for the whole group's lunch by themselves or use Watashi's money. Of course, the leader wants to spend Watashi's money as much as possible, but he is too busy to write a program to calculate the maximum money he can spend from Watashi's reward. Can you help him?

Input

The input contains multiple test cases ( no more than 50 test cases ).
In each test case, first there are two integer, N ( 1 <= N <=30 ) , which is the number of training days, M ( 0 <= M <=10000000 ) , which is the reward money from Watashi.
Then there is a line containing N positive integers with the ith integer indicating the money group C need to pay for the lunch of the ith day. All these integers are no more than 10000000 and integers are seperated by a space.

Output

For each test case, output one line with an integer which is the maximum money group C can spend from Watashi's reward

Sample Input

3 10
8 4 5

Sample Output

9

题意:背包问题,问你给一个背包,求最多能装多少物品。


题解:面对超大背包问题有2种做法,直接01背包复杂度O(N*M)会超时,可以看到n的范围非常小,可以考虑使用搜索。这里搜索同样使用01的思想,”放“或者“不放”。建立一个二叉搜索树。如下图

但是仅仅这样是不可以的。还需要剪枝。

(1)DFS

1.如果结果不能再优则结束所以递归。当答案ans全局变量的值等于背包容量,则结束递归

2.如果容量大于m,结束递归。

3.这里有一个前提,降序排序。如果当前的容量v加上最后一个元素,即最小的元素(a[n-1])都大于m,那么所有元素加上v都大于m,这时应该结束这个子树的递归(不会搜到更优解 )。而重新使他的背包容量置为0,搜索其他的子树。

4。第四个剪枝没有想到。就是如果所有物品体积的和<=背包容量,那么就不用搜索啦。


以下是代码 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 40
int a[N];
int n,m,ans;
#define inf 0x3f3f3f3f
bool cmp(int x,int y)
{
    return x>y;
}
void dfs(int i,int v)
{
    if(ans==m)
    {
        return ;
    }
    if(v>m||i>n)
    {
        return ;
    }
    else
    {
     // printf("%d %d\n",i,v);
        ans=max(ans,v);
    }
    if(v+a[n-1]>m)
    {
        dfs(i+1,a[i]);
        dfs(i+1,0);
        return ;
    }
    else 
    {
        dfs(i+1,v+a[i]);
        dfs(i+1,v);
    }
}
int main()
{
#ifdef CDZSC
	freopen("i.txt","r",stdin);
#endif
    while(~scanf("%d%d",&n,&m))
    {
        ans=-inf;
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        if(sum<=m)
        {
            printf("%d\n",sum);
            continue;
        }
        sort(a,a+n,cmp);
        dfs(0,0);
        printf("%d\n",ans);
    }
    return 0;
}

2.折半搜索

这一题n<=30,那么我们想想如果n<=40,那么这棵搜索树的“体积"是非常可怕的。有2^40种情况,这时候应该缩小问题规模,而折半搜索是将问题规模降低了一半,折半搜索的思想是将这棵树“切”为一半。先枚举出一半树的所有情况,然后再从另一半的树二分查找答案。


代码解释一下:

1.利用二进制的位数变换来表示当前的这个数是否在集合当中:(i>>j)&1,判断第j个元素是否在集合当中。

2.这里我用的是set,不仅自动排序,还能去重。但是使用的时候不能够降序排序(默认升序),这里只能写个结构体重载运算符啦。这一题重点需要解释一下lower_bound函数 若是升序排序那么查找到的是  >=x的位置,而降序排序那么查找到的是  <=x的位置,这一题由题意可知必须要找到 <=x的数字。(话说set的二分查找写起来好轻松啊)

以下是代码 


#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define N 100
int a[N];
struct point
{
	int x;
	point (int _x):x(_x){}
	point (){}
	bool operator<(const point &x1)const
	{
		return this->x>x1.x;
	}
	
};
set<point>st;
int main()
{
#ifdef CDZSC
	freopen("i.txt","r",stdin);
#endif
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		int ans=-inf;
		st.clear();
		for(int i=0;i<n;i++)
			scanf("%d",&a[i]);
		int n2=n/2;
		for(int i=0;i<(1<<n2);i++)
		{
			int sw=0;
			for(int j=0;j<n2;j++)
			{
				if((i>>j)&1)
				{
					sw+=a[j];
				}
			}
			st.insert(point(sw));
		}
		for(int i=0;i<(1<<(n-n2));i++)
		{
			int sw=0;
			for(int j=0;j<n-n2;j++)
			{
				if((i>>j)&1)
				{
					sw+=a[n2+j];
				}
			}
			if(sw<=m)
			{
				ans=max(st.lower_bound(point(m-sw))->x+sw,ans);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}












版权声明:本文为博主原创文章,你若想转载请附上源博客地址。

相关文章推荐

超大的变体背包问题(折半枚举/双向搜索)

来源:挑战程序设计竞赛p162 最近又遇到了这个问题 来总结一下好了 算是补档吧。。。问题? 有N\mathcal N件物品和一个容量为W\mathcal W的背包。第i件物品的费用是wiw_i...
  • DareXK
  • DareXK
  • 2017年03月23日 23:53
  • 189

超大背包问题 折半枚举

超大背包问题:有重量和价值分别为wi,vi的n个物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值。 1 1 1 因为wi,太大,数组开不了,而我们发现n的数量较少,...
  • sky_zdk
  • sky_zdk
  • 2017年03月05日 10:04
  • 144

162_超大背包问题 (双向搜索)

当背包问题的w和v都巨大时,时间复杂度和空间复杂度都不能满足要求,只能利用双向搜索的方法来求解:   首先将数组分成量部分,对第一部分用位操作的方法来枚举所有的子集的w和v(和),然后排序,去重(去...

超大背包问题(枚举二分)

有重量和价值分别为wi和vi的n个物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值 限制条件: 1 1 1 样例输入 4 2 3 1 2 3 4 2 2...

超大多重背包问题

TopCoder SRM 674 div 1 ClassicProblem Problem Statement   This task is about a classic pro...
  • solotzg
  • solotzg
  • 2015年12月29日 16:30
  • 1038

中途相遇法 解决 超大背包问题 pack

Description 【题目描述】蛤布斯有n个物品和一个大小为m的背包,每个物品有大小和价值,它希望你帮它求出背包里最多能放下多少价值的物品。【输入数据】第一行两个整数n,m。接下来n行每行两个整...
  • KenxHe
  • KenxHe
  • 2016年11月17日 16:11
  • 125

《挑战程序设计竞赛中》所讲的超大背包问题

问题描述:有重量和价值分别为 w i ,v i 的 n 个物品。从这些物品中挑选总重量不超过 W 的物品,求所有 挑选方案中价值总和的最大值。其中W的可能非常大。 解题思路:这个也是背包问题,不过...

综合到变态的背包问题]ZOJ 3164 Cookie Choice

zoj 3164 cookie choice 综合背包问题

ZOJ1149 POJ1014 HDU1059 Dividing,多重背包问题

很经典的多重背包问题,大家可以看看《背包九讲》里面的第三讲。 /***********************************************************...
  • neofung
  • neofung
  • 2011年10月02日 20:44
  • 971

ZOJ3164【背包问题(好题)】

%%%%%%%%%%%%%%%岐爷 这一发从来没写过这么旺盛的背包问题。。。 想法很多,但是好难执行。 题意: 有N种饼干,1-N 每种最多想买Ki个,ki等于0的话没有上界 对于第i种饼干的权值是E...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ZOJ 3631超大背包问题(DFS || 折半搜索)
举报原因:
原因补充:

(最多只允许输入30个字)