【问题描述】
czyz暑期集训一共N天。由于jmy和lkf玩游戏总是输,作为惩罚,他需要给oiers买汽水。
jmy最多只能给大家花M元钱。由于每天汽水的价格都不固定,现在给出每天买汽水的花销,我们可以随意选择让jmy哪些天买汽水(当然总花费不能超过M)。请问最多一共能够花掉jmy多少钱呢?
暑假最多不超过40天,jmy给大家花的钱最多有一亿。
【输入格式】
输入第一行有两个整数N,M。 1<=N<=40,0<=M<=100000000。
接下来一行有N个数,第i个数表示第i天的汽水花销。每天汽水的花销p<=100000000。
【输出格式】
输出一行一个整数,表示我们最多能够花掉jmy多少钱。
【输入输出样例一】
drink.in
3 10
1 2 3
drink.out
6
【输入输出样例二】
drink.in
4 10
4 3 5 11
drink.out
9
【数据规模】
对于10%的数据,N<=10。
对于30%的数据,N<=20。
对于60%的数据,N<=30。
对于100%的数据,N<=40。
【10分】 01背包
【60分】 深搜 + 剪枝
直接暴力深搜,考虑如何剪枝:
[1]、将花销数组从大到小排序,这样在搜索时会优先选取花销大的搜索,加快效率。
[2]、记录上一次选取的位置
lst
,则这次只要在
lst+1
~
n
位置进行搜索即可。
[3]、记录花销数组的后缀和,若此时将未搜索到的所有位置的花销全部计入总花销若未超出容量
[4]、记录最小的单天花销
mx
,若剩余的容量小于
mx
,则该情况可以直接排除。
【代码】
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 45;
int a[N], c[N], n, m, Ans, mx = 0x3f3f3f3f;
bool vis[N];
void Dfs(const int &k, const int &lst, const int &w)
{
int tmp = w + c[lst + 1];
if (tmp <= Ans || w > m) return;
if (tmp <= m && tmp >= Ans) return (void)(Ans = tmp);
if (w > Ans) Ans = w;
if (k > n || m - w < mx) return ;
for (int i = lst + 1; i <= n; ++i)
Dfs(k + 1, i, w + a[i]);
}
inline bool cmp(const int &x, const int &y)
{
return x > y;
}
int main()
{
freopen("drink.in", "r", stdin);
freopen("drink.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
sort(a + 1, a + n + 1, cmp);
for (int i = n; i >= 1; --i)
{
c[i] = c[i + 1] + a[i];
if (mx > c[i]) mx = c[i];
}
Dfs(1, 0, 0);
printf("%d\n", Ans);
fclose(stdin); fclose(stdout);
return 0;
}
【100分】 折半搜索
最大数据
N≤40
,我们可以考虑将其分成两部分搜索,用01表示某一天是否买汽水,将两个部分所有的01状态全部搜索出来,总共也就是
2×220
种状态。两个部分每种状态所产生的花销分别计入两个数组
c,d
,那么答案即为
Max(c[i]+d[j])
。
考虑怎样
O(n)
求出
Max(c[i]+d[j])
:将这两个数组分别按从小到大排序,记两个指针
l,r
,初始分别指向
c
数组的起始和
【代码】
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 45, M = (1 << 20) + 5;
int a[N], b[N], c[N], d[N];
int Ans, n, x, m;
inline void Dfs1(const int &k, const int &w)
{
if (w > m) return ;
if (k > a[0]) return (void)(c[++c[0]] = w);
for (int i = 0; i <= 1; ++i)
Dfs1(k + 1, w + (i ? a[k] : 0));
}
inline void Dfs2(const int &k, const int &w)
{
if (w > m) return ;
if (k > b[0]) return (void)(d[++d[0]] = w);
for (int i = 0; i <= 1; ++i)
Dfs2(k + 1, w + (i ? b[k] : 0));
}
inline void CkMax(int &x, const int &y) {if (x < y) x = y;}
int main()
{
freopen("drink.in", "r", stdin);
freopen("drink.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &x);
if (i & 1) a[++a[0]] = x;
else b[++b[0]] = x;
}
Dfs1(1, 0); Dfs2(1, 0);
int l = 0, r = d[0];
sort(c + 1, c + c[0] + 1);
sort(d + 1, d + d[0] + 1);
while ((++l) <= c[0] && r)
{
while (c[l] + d[r] > m) r--;
if (r) CkMax(Ans, c[l] + d[r]);
}
printf("%d\n", Ans);
fclose(stdin); fclose(stdout);
}