题目描述
Y
u
n
o
Yuno
Yuno 刚刚获得了
X
X
X 元奖金, 她想用奖金买一些物品。
购物车里面有
N
N
N 个物品,每个物品的价格是
V
i
V_i
Vi,
Y
u
n
o
Yuno
Yuno 想尽可能地把手头的奖金给花光,所以她要精心选择?些商品,使得其价格总和最接近但又不会超过奖金的金额。
Y
u
n
o
Yuno
Yuno 想知道最少能剩下多少奖金。
输入格式
第一行两个整数
N
N
N 和
X
X
X。
第二行
N
N
N 个整数
V
i
V_i
Vi,表示第
i
i
i 个物品的价格。
输出格式
输出一行整数,表示 Y u n o Yuno Yuno 最后最少可以剩下的钱数。
样例
样例输入1:
4 50
1 2 3 4
样例输出1:
40
样例输入2:
4 5
1 2 3 4
样例输出2:
0
数据范围
对于
20
%
20\%
20% 的数据,
1
≤
N
≤
10
1 \le N \le 10
1≤N≤10。
对于
40
%
40\%
40% 的数据,
1
≤
N
≤
20
1 \le N \le 20
1≤N≤20,
1
≤
X
,
V
i
≤
1
0
4
1 \le X, V_i \le 10^4
1≤X,Vi≤104。
对于
100
%
100\%
100% 的数据,
1
≤
N
≤
40
1 \le N \le 40
1≤N≤40,
1
≤
X
,
V
i
≤
1
0
9
1 \le X, V_i \le 10^9
1≤X,Vi≤109。
题解
对于
40
%
40\%
40% 的数据,我们可以直接使用 背包
或 dfs
做。
对于 100 % 100\% 100% 的数据,由于 1 ≤ N ≤ 40 1 \le N \le 40 1≤N≤40 的数据范围,所以很容易想到折半搜索。
先搜前一半,将搜出可能的钱数放进集合。再搜后一半,也搜出一个钱数,从前面的集合中取出一个最大的数保证这个数加上钱数小于等于 X X X,更新答案。
两次搜索复杂度为 O ( 2 20 ) O(2^{20}) O(220),二分复杂度为 O ( 20 ) O(20) O(20),总复杂度为 O ( 20 × 2 21 ) O(20 \times 2^{21}) O(20×221)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m, t;
int a[50];
int len = 0;
int f[1100000];
int ans = 1e16;
void dfs1(int x, int y){
if(y > m){
return;
}
if(x > t){
f[++ len] = y;
return;
}
dfs1(x + 1, y + a[x]);
dfs1(x + 1, y);
}
void dfs2(int x, int y){
if(y > m){
return;
}
if(x > n){
int tt = upper_bound(f + 1, f + len + 1, m - y) - f - 1;
if(f[tt] + y <= m){
ans = min(ans, m - f[tt] - y);
}
// printf("%d %d %d\n", f[tt], y, m - f[tt] - y);
return;
}
dfs2(x + 1, y + a[x]);
dfs2(x + 1, y);
}
signed main(){
scanf("%lld %lld", &n, &m);
t = n / 2;
for(int i = 1; i <= n; ++ i){
scanf("%lld", &a[i]);
}
dfs1(1, 0);
sort(f + 1, f + len + 1);
dfs2(t + 1, 0);
printf("%lld", ans);
return 0;
}