 /**//*
主要是,一個能量初始值為S 給出n個要消滅的敵人
打敗每個敵人至少要costi ,然后可以休息,獲取體力ri
問能否打敗所有敵人
n <= 22

若能打敗所有敵人,最后的能量值是唯一的
即安排一種順序能打敗所有敵人
對于costi <= ri的,肯定是先打costi比較小的,所以按照costi升序排

對于costi > ri的,我是按costi降序排,錯誤
如
5
5 2
3 3
然后按照 costi - ri 升序排,也錯
如
5
3 2
5 3

正確的是按照ri降序排
解題報告說跟zoj 3077類似,證明挺有啟示的
不是一般性,假設答案的順序是1,2..,n
k-1
必有 S - ∑(costi - ri) >= costk
i=1
我們可以通過交換所有rj < rj+1的敵人,但最后結果一樣!!!
因為 S' >= costj , S' - (costj - rj) >= costj+1
=> S' >= costj+1 , S' - (costj+1 - rj+1) >= costj
所以像冒泡排序那樣交換,最后形成一個r降序的序列,但效果一樣

而zoj 3077按照b降序后,dp選擇一部分出來
效果跟最優解是一樣的(最優解其實就是一個集合+一定順序,而這個順序跟按照b降序效果一樣,
而選擇這個集合就是dp了)

如果沒有沒有排序,直接dp就保證不了無后效性
我想無后效性就是 先選a再選b 效果跟 先選b再選a,不會因為選擇了a就不能選擇b了
所以普通背包問題,按照編號的順序進行dp就行了
中大第三本”數字游戲“ 也是先排序,在dp選擇
*/
#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
#include<stack>
#include<queue>
#include<cmath>
#include<string>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<set>
#include<list>

using namespace std;

const int INF = 1000000000;

int main()
  {
int T;
 for(scanf("%d",&T);T--;) {
int n , S;
vector<pair<int,int> > less , _less;
scanf("%d%d",&n,&S);
 for(int i = 0 ; i < n ; i++) {
int p[3] , r;
scanf("%d%d%d%d",&p[0],&p[1],&p[2],&r);
int dp[10];
fill(dp,dp+10,INF);
dp[0] = 0;
 for(int i = 0 ; i < 3 ; i++) {
int get = 3 - i;
 for(int j = 0 ; j + get <= 9 ; j++) {
dp[j+get] = min(dp[j+get] , dp[j] + p[i]);
}
}
int cost = INF;
 for(int j = 7 ; j <= 9 ;j++) {
cost = min(cost , dp[j]);
}
 if(cost < r ) {
less.push_back(make_pair(cost , r));
}
 else {
_less.push_back(make_pair(r , cost));
}

}
sort(less.begin() , less.end());
sort(_less.begin() , _less.end(),greater<pair<int,int> >());
 for(vector<pair<int,int> >::iterator it = less.begin() ; it != less.end() ; it++) {
S -= it->first;
if(S <= 0)break;
S += it->second;
}
 for(vector<pair<int,int> >::iterator it = _less.begin() ; it != _less.end() ; it++) {
S -= it->second;
if(S <= 0)break;
S += it->first;
}
 if(S > 0) {
printf("%d\n",S);
}
 else {
puts("no");
}
}
return 0;
}

|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|