这个题第一眼以为只是一个简单的序列型DP问题,于是快速的写下了下面的代码。
#include <iostream> using namespace std; /* 看着只是简单的序列DP..不知道有没有坑 DP[k]表示的是有前k个活动可以选择时的答案 */ struct state { int health;//体力 永远大于0 int wisdom;//智力 最后大于70 int product;//乘积 void calc(){ product = health * wisdom; } }; struct act { int dh; int dw; }; state dp[400+5]; act acts[400+5]; int n;//一共的活动数目 void init(){ cin>>n; cin>>dp[0].health>>dp[0].wisdom; dp[0].calc(); for (int i = 1; i <= n; ++i) { cin>>acts[i].dh>>acts[i].dw; } } void build(){ for (int i = 1; i <= n; ++i) { // int h = dp[i-1].health + acts[i].dh; int w = dp[i-1].wisdom + acts[i].dw; if( h > 0 ){ if( h * w >= dp[i-1].product){ dp[i].health = h; dp[i].wisdom = w; dp[i].calc(); }else{ dp[i] = dp[i-1]; } }else{ dp[i] = dp[i-1]; } } if(dp[n].wisdom > 70 and dp[n].health > 0) cout<<dp[n].product<<endl; else cout<<"Death"<<endl; } int main(int argc, char const *argv[]) { init(); build(); return 0; }
这个代码在是求和的时候是可以的,因为前面和最大的情况,+dh +dw仍然也是最大的。
但是在乘积的时候就不然了,例子不好举。但是很明显 当达到积最大有两种情况时,这两种选哪个作为dp值在不知道下一个活动时是无法确定的。
所以就想到,确定一个变量,让一维的DP化为二维。
dp[i][j]表示在前i个活动可以选择时,体力值为j时,最大的智力值是多少。
所以
dp[i][j] =
max {(dp[i-1][j-acts[i].h] + acts[i].w) //选上, (dp[i-1][j]) //不选}
这里有一个细节要注意就是,初始状态是1000,那么数组第二维要开到32000+50才行。(。。最后才发现是这个地方错了 还不报RE真是可恶。。)
还有一个非常重要的细节就是 智力值在过程中是可以为负数的,所以我们初始化时要把智力值全部设置为-INF才可以,这样我们就可以根据智力值与-INF的关系知道当前处理的状态的是经过一系列过程转化来的智力值,还是根本没有办法达到的智力值。(此处也可以在搞一个bool型的二维数组来记录这两种状态,也不麻烦而且更美观)
另外还有一个地方可以优化 就是内层循环的次数,我们可以采取迭代递增的方式,让curMax始终是当前可能的最大的体力值,但是实际上没什么太大的效果。
struct act { int dh; int dw; }; // state dp[400+5]; int dp[400+5][32000+50]={0}; act acts[500+5]; int n;//一共的活动数目 void init(){ cin>>n; for (int i = 0; i <= n; ++i) { cin>>acts[i].dh>>acts[i].dw; }//注意acts[0]存储的是初始情况 } void build(){ //初始化为负无穷 以来判断智力值是转化来的 还是根本没有被利用 memset(dp,-50000,sizeof(dp)); dp[0][acts[0].dh] = acts[0].dw;//初始化 int curMax = 31000+50;//固定的curMax 注意31000 = 30000+1000 for (int i = 1; i <= n; ++i) { for (int j = 0; j <= curMax; ++j) { if(j >= acts[i].dh and dp[i-1][j-acts[i].dh] > -50000){ //体力值大于0 且 智力值是有效的 dp[i][j] = max(dp[i-1][j], dp[i-1][j-acts[i].dh]+acts[i].dw); } else dp[i][j] = dp[i-1][j]; } } int res = 0; //找到最大的 for (int j = 1; j <= curMax; ++j) { if(dp[n][j] > 70){ res = max(res,dp[n][j] * j); } } if(res) cout<<res<<endl; else cout<<"Death"<<endl; } int main(int argc, char const *argv[]) { init(); build(); return 0; }