/**//* 主要是,一个能量初始值为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
搜索
最新评论
|
|