这两题卡我一天, 不过div3不ak说不过去
题意(easy-version):
- 有i个单位
- 现有n件物品, 每个物品有两个属性a, b,至少有a个单位才能购置这个件物品, b购置这件物品后你会得到不确定的收益b(b 有可能小于0), 即目前的单位数+b
- 现在询问你能否购置所有的物品
>> face <<
前置技能: 贪心
原来想先按照物品的前提条件排序, 然后再满足所有前提条件的物品里面选一个收益最高的, 结果当然wa; 这样想仅针对是正数的情况, 对于收益是负数的情况怎么办呢
我们假设有两件商品 x , y x, y x,y, 目前有cur元, 且购置了x后无法购置y , 即:
c u r + x . s e c o n d ≥ y . s e c o n d c u r + y . s e c o n d < x . f i r s t 合 并 之 后 即 为 : x . f i r s t + x . s e c o n d > y . f i r s t + y . s e c o n d cur+x.second\geq y.second\\ cur+y.second <x.first\\ 合并之后即为:\\ x.first + x.second > y.first + y.second cur+x.second≥y.secondcur+y.second<x.first合并之后即为:x.first+x.second>y.first+y.second
于是我们就可以按照这个规律排序
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define eps 1e-8
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl;
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
int nxt()
{
int ret;
scanf("%d", &ret);
return ret;
}
vector<pii> neg, pos, cppo, cpne;
pii tmp;
int main() {
int n = nxt(), cur = nxt();
_rep(i, 1, n) {
tmp.first = nxt(), tmp.second = nxt();
if (tmp.second < 0) neg.emplace_back(tmp);
else pos.emplace_back(tmp);
}
sort(all(neg), [](const pii a, const pii b) {return a.first == b.first ? a.second > b.second: a.first < b.first; });
sort(all(pos), [](const pii a, const pii b) {return a.first == b.first ? a.second > b.second : a.first < b.first; });
bool okay = 1;
_rep(i, 1, n) {
cppo = pos;
cpne = neg;
int th = upper_bound(pos.begin(), pos.end(), pii{ cur, -oo }, [](const pii a, const pii b) {return a.first == b.first ? a.second > b.second : a.first < b.first; }) - pos.begin();
if (th) {//有正数的
sort(cppo.begin(), cppo.begin() + th, [](const pii a, const pii b) {return a.second > b.second; });
cur += cppo[0].second;
pos.erase(find(all(pos), cppo[0]));
}
else {
int th = upper_bound(all(neg), pii{ cur, -oo }, [](const pii a, const pii b) {return a.first == b.first ? a.second > b.second : a.first < b.first; }) - neg.begin();
if (th == 0) {
okay = 0;
break;
}
sort(cpne.begin(), cpne.begin() + th, [](const pii a, const pii b) {return a.first + a.second > b.second + b.first; });
cur += cpne[0].second;
neg.erase(find(all(neg), cpne[0]));
if (cur < 0) {
okay = 0;
break;
}
}
}
okay ? cout << "YES" << endl : cout << "NO" << endl;
}
题意(hard-version):
- 有i个单位
- 现有n件物品, 每个物品有两个属性a, b,至少有a个单位才能购置这个件物品, b购置这件物品后你会得到不确定的收益b(b 有可能小于0), 即目前的单位数+b
- 先询问最多能购置多少物品
>> face <<
前置技能: 贪心 + dp
有注意到这题商品数n最多只有100, 而且最大的cur最多也就60000, 完全可以dp来搞
这题我一直写的贪心, 怎么都过不了, 看网上题解写的贪心都是可反悔, 唉, 还是dp算了
状态: d p [ i ] [ j ] dp[i][j] dp[i][j] 代表已经购置了i件物品, 现在还剩j块钱
初始化: d p [ 0 ] [ c u r ] = a n s dp[0][cur] = ans dp[0][cur]=ans: ans是贪到的正收益的物件数,cur是加上正收益后的钱;
转移:
{ d p [ i + 1 ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i + 1 ] [ j ] ) → 不 买 d p [ i + 1 ] [ j + n e g [ i ] . s e c o n d ] = m a x ( d p [ i + 1 ] [ j + n e g [ i ] . s e c o n d ] , d p [ i ] [ j ] + 1 ) → 买 他 \begin{cases} dp[i+1][j] = max(dp[i][j], dp[i+1][j])\to不买 \\ dp[i+1][j+neg[i].second] = max(dp[i+1][j+neg[i].second], dp[i][j]+1)\to 买他 \end{cases} {dp[i+1][j]=max(dp[i][j],dp[i+1][j])→不买dp[i+1][j+neg[i].second]=max(dp[i+1][j+neg[i].second],dp[i][j]+1)→买他
有注意到每个dp[i]只与dp[i-1]有关, 所以还可以用滚动数组优化空间, 但是时间复杂度还是不变的
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define eps 1e-8
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl;
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
int nxt()
{
int ret;
scanf("%d", &ret);
return ret;
}
vector<pii> neg, pos, cppo, cpne;
int dp[60000 + 3];
pii tmp;
int main()
{
int n = nxt(), cur = nxt();
_rep(i, 1, n)
{
tmp.first = nxt(), tmp.second = nxt();
if (tmp.second < 0)
neg.emplace_back(tmp);
else
pos.emplace_back(tmp);
}
sort(all(neg), [](const pii a, const pii b) { return a.first + a.second > b.first + b.second; });
sort(all(pos), [](const pii a, const pii b) { return a.first == b.first ? a.second > b.second : a.first < b.first; });
bool okay = 1;
int ans = 0;
while (1)
{
cppo = pos;
int th = upper_bound(pos.begin(), pos.end(), pii{cur, -oo}, [](const pii a, const pii b) { return a.first == b.first ? a.second > b.second : a.first < b.first; }) - pos.begin();
if (th)
{ //有正数的
sort(cppo.begin(), cppo.begin() + th, [](const pii a, const pii b) { return a.second > b.second; });
cur += cppo[0].second;
pos.erase(find(all(pos), cppo[0]));
++ans;
}
else break;
}
vector<vector<int>> dp(neg.size() + 1, vector<int>(cur + 1, 0));
dp[0][cur] = ans;
int res = 0;
_for(i, 0, neg.size())
{
_rep(j, 0, cur)
{
if (j >= max(neg[i].first, -neg[i].second))
{
dp[i + 1][j + neg[i].second] = max(dp[i + 1][j + neg[i].second], dp[i][j] + 1);
}
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);
}
}
_rep(i, 0, cur)
{
res = max(res, dp[neg.size()][i]);
}
cout << res << endl;
}