这道题乍一看是普通的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背包(折半枚举)的更多相关文章

  1. nyoj 1091 还是01背包(超大数dp)

    nyoj 1091 还是01背包 描述 有n个重量和价值分别为 wi 和 vi 的物品,从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值 1 <= n <=40 1 ...

  2. CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。

    1514: Packs Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 61  Solved: 4[Submit][Status][Web Board] ...

  3. Nyoj 三国志(dijkstra+01背包)

    描述 <三国志>是一款很经典的经营策略类游戏.我们的小白同学是这款游戏的忠实玩家.现在他把游戏简化一下,地图上只有他一方势力,现在他只有一个城池,而他周边有一些无人占的空城,但是这些空城中 ...

  4. hdu 5887 Herbs Gathering (dfs+剪枝 or 超大01背包)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5887 题解:这题一看像是背包但是显然背包容量太大了所以可以考虑用dfs+剪枝,贪心得到的不 ...

  5. E - Knapsack 2 题解(超大01背包)

    题目链接 题目大意 给你一n(n<=100)个物品,物品价值最大为1e3,物品体积最多为1e9,背包最大为1e9 题目思路 如果按照平常的背包来算那么时间复杂度直接O(1e11) 这个你观察就发 ...

  6. POJ 1837 Balance(01背包变形, 枚举DP)

    Q: dp 数组应该怎么设置? A: dp[i][j] 表示前 i 件物品放入天平后形成平衡度为 j 的方案数 题意: 有一个天平, 天平的两侧可以挂上重物, 给定 C 个钩子和G个秤砣. 2 4 - ...

  7. 中南林业大学校赛 I 背包问题 ( 折半枚举 || 01背包递归写法 )

    题目链接 题意 : 中文题 分析 :  价值和重量都太过于大,所以采用折半枚举的方法,详细可以看挑战的超大背包问题 由于 n <= 30 那么可以不必直接记录状态来优化,面对每个用例 直接采用递 ...

  8. (容量超大)or(容量及价值)超大背包问题 ( 折半枚举 || 改变 dp 意义 )

    题意 : 以下两个问题的物品都只能取有且只有一次 ① 给你 N 个物品,所有物品的价值总和不会超过 5000, 单个物品的价格就可达 10^10 ,背包容量为 B ② 给你 N (N ≤ 40 ) 个 ...

  9. nyoj 203 三国志 dijkstra+01背包

    题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=203 思路:先求点0到每个点的最短距离,dijkstra算法,然后就是01背包了 我奇怪的 ...

随机推荐

  1. linux网络编程常用头文件

    sys/types.h:数据类型定义 sys/socket.h:提供socket函数及数据结构 netinet/in.h:定义数据结构sockaddr_in arpa/inet.h:提供IP地址转换函 ...

  2. sae crop 文档

    原文是google缓存:http://webcache.googleusercontent.com/search?q=cache:MD_FP-G6RI8J:sae.sina.com.cn/%3Fm%3 ...

  3. JQuery验证input

    jsp中表单如下: <form method="post" action="AddPlayer"> <div class = "on ...

  4. 1 Two Sum(找和为target的两个数字下标Medium)

    题目意思:给一个数组,找到和为target的两个元素的序号,并且只有一组这样的元素 思路:map<int,int>(nums[i],i+1),然后从后往前循环,用count找,比较i+1 ...

  5. 解决WebService本地访问正常,远程无法访问的问题

    发布webservice后部署到自己的服务器上,然后本机,外网远程访问都没事,在用户服务器上部署后只能本机访问,远端访问不了,通过网络搜索到下面方法,但改后仍然不行.原来在自己服务器部署时是在默认网站 ...

  6. javascript 键值对

    <script type="text/javascript"> var arr = new Array(); arr['cn'] = '中国'; arr['usa'] ...

  7. day06

    一.configparser模块 configparser用于处理特定格式的文件,其本质上是利用open来操作文件 1.获取所有节点 import configparser config = conf ...

  8. 如何让你的Python程序支持多语言

    如何让你的Python程序支持多语言 本文介绍如何通过Python标准库gettext帮助你的程序支持多语言. 代码例子 import random guessesTaken = 0 print(_( ...

  9. Net Core 项目实战之权限管理系统(0)

    0 前言 Net Core 项目实战之权限管理系统(0) 无中生有   0 http://www.cnblogs.com/fonour/p/5848933.html 学习的最好方法就是动手去做,这里以 ...

  10. 『Python』 多线程 端口扫描器

    0x 00 Before Coding 当端口打开时,向端口发送 TCP SYN 请求,会返回一个 ACK 响应: 当端口关闭,返回的是 RST 响应: 0x 01 Coding  可以用 socke ...