NYOJ 1091 超大01背包(折半枚举)
这道题乍一看是普通的01背包,最最基础的,但是仔细一看数据,发现普通的根本没法做,仔细观察数组发现n比较小,利用这个特点将它划分为前半部分和后半部分这样就好了,当时在网上找题解,找不到,后来在挑战程序设计上找到了这个题,就拿来引用一下
挑选物品的方法总从2^n中,直接枚举肯定不行,因为n最大为40,但是如果n为20就可以了,这时候就要用到折半枚举,先枚举前一半,在枚举后一半。先把前把部分的选取方法对应的重量和价值总和记为w1, v1,这样后半部分寻找w2 <= W - w1时 使v2最大的选取方法就好了。
因此,我们要思考从枚举得到的(w2, v2)的集合中高效寻找max{v2|w2<=W'}的方法。首先,显然我们可以排除所有w2[i] <= w2[j]并且v2[i] >= v2[j] 的 j。 这一点可以按照w2,v2的字典序排列后做到。此后剩余的元素都满足w2[i] < w2[j] , v2[i] < v2[j], 要计算max{v2|w2<=W'}的话,只要寻找满足w2[i]<=W'的最大的i就可以了。这可以利用二分搜索完成。剩余的元素个数为M的话,一次搜索要用log(M)的时间,可以解决
代码如下:
方法一(折半枚举):
- #include <iostream>
- #include <algorithm>
- #include <cstdio>
- #define Max(a,b) a>b?a:b
- #define INF 10000000000000000
- using namespace std;
- typedef long long LL;
- const int MAX = ;
- LL weight[MAX], value[MAX];
- LL W;
- pair<LL, LL> ps[ << (MAX / )];
- int n;
- void slove()
- {
- //枚举前半部分
- int n2 = n / ;
- for (int i = ; i < << n2; i++)//前半部分的枚举总数为 2^(n/2);
- {
- LL sw = , sv = ;
- //每种结果选取特定的价值和重量(i.e 一共2个东西,就一共四种情况,都不选,选第一个,选第二个,都选)
- for (int j = ; j < n2; j++)
- {
- if (i >> j & )
- {
- sw += weight[j];
- sv += value[j];
- }
- }
- ps[i] = make_pair(sw, sv);//加入到ps数组中
- }
- //对ps排序
- sort(ps, ps + ( << n2));
- //ps 去重
- int m = ;
- for (int i = ; i < << n2; i++)
- if (ps[m - ].second < ps[i].second)
- ps[m++] = ps[i];
- LL res = ;//保存结果
- //枚举后半部分, 并且找到最优解
- for (int i = ; i < << (n - n2); i++)//同样枚举的总个数
- {
- LL sw = , sv = ;
- for (int j = ; j < n - n2; j++)//和前半部分的一样
- {
- if (i >> j & )
- {
- sw += weight[n2 + j];
- sv += value[n2 + j];
- }
- }
- if (sw <= W)//加个判断求解最大价值,只有小于背包容量的时候
- {
- LL tv = (lower_bound(ps, ps + m, make_pair(W - sw, INF)) - )->second;//找到前半部分对应的value
- res = Max(res, sv + tv);
- }
- }
- printf("%lld\n", res);
- }
- int main()
- {
- while (~scanf("%d %lld", &n, &W))
- {
- for (int i = ; i < n; i++)
- scanf("%lld %lld", &weight[i], &value[i]);
- slove();
- }
- return ;
- }
这个题也可以用搜做来做,搜索反而来的更快,因为n比较小
方法二(搜索):
- #include <stdio.h>
- #include <string.h>
- #define Max(a, b) a > b ? a : b
- const int MAX = ;
- long long weight[MAX], value[MAX], sw[MAX], sv[MAX];
- long long W, n, ans;
- //i表示当前取到第n-i个,cnt 表示当前的总value, w当前背包剩余的空间
- void dfs(int i, long long cnt, long long w)
- {
- if (i == )//取到最后
- {
- ans = Max(ans, cnt);
- return;
- }
- if (w == || cnt + sv[i] < ans)//背包满或者当前总的加上这个前i个的总价值小于当前的总value,这步是剪枝
- return ;
- if (w >= sw[i])//因为是从上往下找的,所以只要当前容量能装下前i个的和,所以这时一定是最大的
- {
- cnt += sv[i];
- ans = Max(ans, cnt);
- w = ;
- return ;
- }
- if (w > weight[i])//深搜两种状态
- dfs(i - , cnt + value[i], w - weight[i]);//相当于01背包中的两种状态
- dfs(i - , cnt, w);
- }
- int main()
- {
- while (~scanf("%d %lld", &n, &W))
- {
- memset(sw, , sizeof(weight));
- memset(sv, , sizeof(value));
- ans = ;
- for (int i = ; i <= n; i++)
- {
- scanf("%lld %lld", &weight[i], &value[i]);
- sw[i] = sw[i - ] + weight[i];
- sv[i] = sv[i - ] + value[i];
- }
- dfs(n, , W);
- printf("%lld\n", ans);
- }
- return ;
- }
NYOJ 1091 超大01背包(折半枚举)的更多相关文章
- nyoj 1091 还是01背包(超大数dp)
nyoj 1091 还是01背包 描述 有n个重量和价值分别为 wi 和 vi 的物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值 1 <= n <=40 1 ...
- CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。
1514: Packs Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 61 Solved: 4[Submit][Status][Web Board] ...
- Nyoj 三国志(dijkstra+01背包)
描述 <三国志>是一款很经典的经营策略类游戏.我们的小白同学是这款游戏的忠实玩家.现在他把游戏简化一下,地图上只有他一方势力,现在他只有一个城池,而他周边有一些无人占的空城,但是这些空城中 ...
- hdu 5887 Herbs Gathering (dfs+剪枝 or 超大01背包)
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5887 题解:这题一看像是背包但是显然背包容量太大了所以可以考虑用dfs+剪枝,贪心得到的不 ...
- E - Knapsack 2 题解(超大01背包)
题目链接 题目大意 给你一n(n<=100)个物品,物品价值最大为1e3,物品体积最多为1e9,背包最大为1e9 题目思路 如果按照平常的背包来算那么时间复杂度直接O(1e11) 这个你观察就发 ...
- POJ 1837 Balance(01背包变形, 枚举DP)
Q: dp 数组应该怎么设置? A: dp[i][j] 表示前 i 件物品放入天平后形成平衡度为 j 的方案数 题意: 有一个天平, 天平的两侧可以挂上重物, 给定 C 个钩子和G个秤砣. 2 4 - ...
- 中南林业大学校赛 I 背包问题 ( 折半枚举 || 01背包递归写法 )
题目链接 题意 : 中文题 分析 : 价值和重量都太过于大,所以采用折半枚举的方法,详细可以看挑战的超大背包问题 由于 n <= 30 那么可以不必直接记录状态来优化,面对每个用例 直接采用递 ...
- (容量超大)or(容量及价值)超大背包问题 ( 折半枚举 || 改变 dp 意义 )
题意 : 以下两个问题的物品都只能取有且只有一次 ① 给你 N 个物品,所有物品的价值总和不会超过 5000, 单个物品的价格就可达 10^10 ,背包容量为 B ② 给你 N (N ≤ 40 ) 个 ...
- nyoj 203 三国志 dijkstra+01背包
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=203 思路:先求点0到每个点的最短距离,dijkstra算法,然后就是01背包了 我奇怪的 ...
随机推荐
- SVN 不能提交, 看不到日志, 出现乱码. 解决方案.
需要工具 sprite3: 点这里下载. 解决问题 如本文标题所写. 我遇到过几次一样的问题, 每次都很蛋疼的把目录重新检出, 浪费时间, 又伤了脾气. 下面是我在百度经验找到的一片帖子, 效果杠杠的 ...
- Android JNI 之 环境安装
在配置环境之前,我们得了解 JNI 和NDK JNI JNI是Java Native Interface的缩写,中文为JAVA本地调用.它提供了若干的API实现了和Java和其他语言的通信(主要是C& ...
- Thinkphp 验证码、文件上传
一.验证码 验证码参数 例题:登录时验证下验证码 LoginController.class.php <?php namespace Home\Controller; use Think\Con ...
- git log友好显示
查看commit 提交日志 $ git log $git log --pretty=oneline $git reflog 显示所有提交记录,包括已经回退的提交,如图:提交了abc 和 bb 然后回退 ...
- 三种php连接access数据库方法
种是利用php的pdo,一种是odbc,com接口来与access数据库连接.利用pdo与access数据库连接 $path ="f:fontwww.jb51.netspiderresult ...
- 【转】Windows平台下Git服务器搭建
Windows平台下Git服务器搭建 Posted on 2015-05-18 21:29 阿祥当码农 阅读(7637) 评论(0) 编辑 收藏 该文章转自:http://www.codeceo.co ...
- Scut 进阶:网络模型拓扑
处理消息流程: 关于是否能用 json 串作为 response? 在最后写消息的时候要加上控制选项,将Response类型,事直接以字节流,还是转json串再转字节流的方式进行编码了,如果要转jso ...
- select m objects from n objects randomly
Q: how to select m objects randomly from n objects with euqal possibility? A: allocate an array of m ...
- Asteroids
http://poj.org/problem?id=3041 #include<cstdio> #include<cstring> #include<algorithm& ...
- bzoj2730 [HNOI2012]矿场搭建 (UVAlive5135 Mining Your Own Business)
2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1147 Solved: 528[Submit][Statu ...