NYG的揹包 (貪心)
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為標準從大到小排序