9.19
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int N = 1e5+4, INF = 0x3f3f3f3f;
struct Node {
LL aa, bb, cc;
friend bool operator <(const Node &p, const Node &q) {
if(p.cc>=0 && q.cc>=0) return p.aa < q.aa;
if(p.cc<0 && q.cc<0) return p.bb > q.bb;
return p.cc>=0;
}
}aa[N];
int n,T;
LL V;
inline int read() {
int x=0; char cc=getchar();
while (cc<'0'||cc>'9') cc=getchar();
while (cc>='0'&&cc<='9') x=x*10+cc-'0',cc=getchar();
return x;
}
int main() {
freopen("backpack.in","r",stdin);
freopen("backpack.out","w",stdout);
T = read();
while (T--) {
n = read(), V = read();
for(register int i=1; i<=n; ++i)
aa[i].aa = read(), aa[i].bb = read(), aa[i].cc = aa[i].bb - aa[i].aa;
sort(aa+1, aa+n+1);
bool flag = true;
for(register int i=1; i<=n; ++i) {
if(V < aa[i].aa) {flag = false; break;}
V += aa[i].cc;
}
puts(flag ? "Yes" : "No");
}
return 0;
}
此题很有复习价值,本题中有几个点重点:
【1】 对于可行问题要往贪心方向思考
【2】 在想贪心方案是需要严格证明,宁可画上一定时间(但要控制再半小时以内)去证明正确性 , 找反例
【3】 在想贪心情况时 , 要落到实处 ,若觉得一个策略不对则需要通过题目特性落到实处看其是否正确;找反例时先感性想想反例出现情况,在去构造对应数据看是否能否定当前结论
在解决此题时 , 由于放入体积与增加体积,而且题目问题是是否可解,可使人想到贪心;
而贪心策略中,由于要是其放得下,所以可以想到先放有贡献(增加体积)的情况 , 然后猜测是贪心顺序,以a排序?b排序?还是b-a?此时发现它最后加完的体积是一个定值 , 而其体积也在不断增加,那么应该是a越大的放到越后面能跟保险的放下,然而是否会有反例,根据其特性:v会应为b - a > 0 而不断增大,显而易见策略正确;
而此题头疼的是b - a < 0 时的策略 : 还是按照上面所述的思路 , a?,b?,b-a?,按照从大到小?从小到大?貌似从正面不好想,但有个性质时确定的:
最后的体积为定值
那么从次特性思考,倒着来想:
最后的状态是:
V + b1 + b2 + b3 + … + bn >= a1 + a2 + a3 + … + an
那么最后一次选择便是选择先将左式减去对于的b , 在不等式成立的情况下在减去对应得a ,由于后面加上的是b - a < 0 , 那么每次减去一组对应的a,b时左式减右式的值会越来越大 , 所以只要保证每次删的b最小,时左式减小的尽量少,那么等式则月容易成立,所以嘛。。。反过来想 , 不就是。。。以b为标准从大到小排序。。。
那么全程的贪心策略则出来了:
对于 a - b > 0 的情况 以a为标准从小到大排序
对于 a - b < 0 的情况 以b为标准从大到小排序