abc056d <贪心 / 二分+DP+bitset>
https://atcoder.jp/contests/abc056/tasks/arc070_b
solution1: 贪心
// https://atcoder.jp/contests/abc056/tasks/arc070_b
// 查到多种做法 二分 / dp ...
// 参考 https://blog.csdn.net/sdz20172133/article/details/90739645
// 这里是贪心做法
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 5e3 + 10;
int a[N];
void solv()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
int pos = lower_bound(a + 1, a + 1 + n, k) - a - 1;
int ans = pos;
for (int i = 1; i <= pos; i++)
{
int s = 0;
for (int j = pos; j; j--)
{
if (i == j || s >= k - a[j]) continue;
s += a[j];
if (s >= k - a[i])
{
ans --;
break;
}
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--)
{
solv();
}
return 0;
}
solution2: DP
注意这种DP的属性值为bool值的情况, 使用bitset优化效果极佳!
bitset使用+DP优化案例参考传送门
// https://atcoder.jp/contests/abc056/submissions/me
// 二分 + DP + bitset优化DP
// check0 为不用bitset优化的DP,
// check1 为使用bitset的DP,
// 二者耗时实测相差约30倍
#include <iostream>
#include <algorithm>
#include <vector>
#include <bitset>
using namespace std;
typedef long long LL;
const int N = 5e3 + 10;
int a[N];
int n, k, pos;
// DP进行检查
// 1058ms, 6524KB
bool check0(int x)
{
if (a[x] >= k) return true;
// 检查 a[1~pos] 除去 a[x] 外, 能否凑出 k-a[x] ~ k-1 中的数,
// 如果可以, 则 a[x] 为必须的元素, 反之不必须(返回false)
vector<vector<bool>> f(pos+1, vector<bool>(k+1, false));
f[0][0] = true;
// 考虑前i个元素(不含第x个), 能否凑出数字j
for (int i = 1; i <= pos; i ++)
{
for (int j = 0; j < k; j ++)
{
f[i][j] = f[i][j] | f[i-1][j];
if (j >= a[i] && i != x) f[i][j] = f[i][j] | f[i-1][j-a[i]];
}
}
// 检查dp结果
for (int i = k-a[x]; i < k; i ++) if (f[pos][i]) return true; // 必须
return false; // 非必需
}
// 用bitset对DP进行优化
// 理论上将时间复杂度 /32, 实测也确实如此
// 31ms, 3656KB
bool check1(int x)
{
if (a[x] >= k) return true;
bitset<N*2> f; // 默认初始化每位都为0
f.set(0); // 令f[0] = 1;
// 仅使用1维, 这是对check0中的dp的滚动优化
// 因为这里的bitset是将 N*2 位同时进行操作的, 因而也无需备份
// 注意更新顺序与check0不同, 这里展开写成check0的形式是 f[i][j+a[i]] |= f[i-1][j] ,
// 不过因为bitset的运算, 可以直接将j这一维度同时进行计算, 也就是用 ' <<a[i] ' 表示了 ' +a[i] '
for (int i = 1; i <= pos; i ++)
if (i != x)
f |= f << a[i];
// 检查dp结果
for (int i = k-a[x]; i < k; i ++) if (f[i]) return true;
return false;
}
void solv()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++) cin >> a[i];
sort(a+1, a+1+n);
// 找到 a[i] >= k 的位置, 这样的a[i]一定是必须的
pos = lower_bound(a+1, a+1+n, k) - a;
int l = 1, r = pos;
// 找到第一个必要的元素 [... , 其左侧均为非必要元素
while (l < r)
{
int mid = (l + r) / 2;
if (check1(mid)) r = mid;
else l = mid + 1;
}
int ans = l - 1; // a[l~n] 为必须元素, 因而非必需元素为 a[1~l-1]
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T = 1;
// cin >> T;
while (T --)
{
solv();
}
return 0;
}
本文来自博客园,作者:O2iginal,转载请注明原文链接:https://www.cnblogs.com/o2iginal/p/17496394.html