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背包了 我奇怪的 ...
随机推荐
- 关于表单的jQuery练习
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...
- js变量声明与赋值以及函数声明
if (!("a" in window)) { var a = 1; } alert(a); 结果:undefined 分析: 首先,所有的全局变量都是window的属性,语句 v ...
- JVM笔记-逃逸分析
参考: http://www.iteye.com/topic/473355http://blog.sina.com.cn/s/blog_4b6047bc01000avq.html 什么是逃逸分析(Es ...
- 一个测ip和端口是否联通的工具类
public class TestIp { public static void main(String[] args) { Socket connect = new Socket(); try { ...
- Spring ApplicationContext的国际化支持
ApplicationContext接口继承MessageSource接口,因此具备国际化功能.下面是MessageSource接口定义的三个国际化方法. >String getMessage( ...
- nutch fetcher.server.delay
1 配置因素 <property> <name>fetcher.server.delay</name> <value>0.0</value&g ...
- cf C. Hacking Cypher
http://codeforces.com/contest/490/problem/C 题意:把一个很大的数分成两部分,前一部分可以被a整除,后一部分可以被b整除,如果存在输出这两部分,两部分都不能含 ...
- cf D. Vessels
http://codeforces.com/contest/371/problem/D 第一遍写的超时了,然后看了别人的代码,思路都是找一个点的根,在往里面加水的时候碗中的水满的时候建立联系.查询的时 ...
- POJ1860 Currency Exchange(最短路)
题目链接. 分析: 以前没做出来,今天看了一遍题竟然直接A了.出乎意料. 大意是这样,给定不同的金币的编号,以及他们之间的汇率.手续费,求有没有可能通过不断转换而盈利. 直接用Bellman-ford ...
- HDU 2444 The Accomodation of Students(判断是否可图 + 二分图)
题目大意:有一群人他们有一些关系,比如A认识B, B认识C, 但是这并不意味值A和C认识.现在给你所有互相认识的学生,你的任务是把所有的学生分成两个一组, 住在一个双人房里.相互认识的同学可以住在一个 ...