http://poj.org/problem?id=2184 Cow Exhibition
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
#define maxn 105
int dp[200005];
int s[maxn];
int f[maxn];
int N;
/*
思路:想要s和f之和都大于0,并且两者相加结果最大
那么可以利用dp[i]的下标记录i当前有多少smartness,dp[i]的值记录在当前smartness的限制下能获得的最大funness值
为了规避数组不能用负数来访问(有必要的,假如之前smartness负的很厉害,但是funness也很高,这时候出现一个samrtness超高同时funness一般般的往负数方向靠,那不就可以获得获得最优值的可能了吗)
需要将下界提高到100000,因为N最多100,单体smartness值最低-1000.
之后从100000的位置来寻求最优解就行了
Ps:注意此时dp值应该大于0并且要减去下界100000
最优解判断:if(j-100000+dp[j]>ans) ans=j-100000+dp[j];
所以做一只既幽默又聪明的奶牛吧!
*/
/*
But! -----------------这一段最好AC不了再看,关于RE的情况
这一题我RE好好多次,原因是因为访问了下标为负的数组地址,如(dp[-1])
如果没有用for将dp全体元素置为-0x3f3f3f3f,而是用 memset(dp,-0x3f3f3f3f,sizeof(dp));
会导致 if(dp[j-s[i]]>-0x3f3f3f3f)绝对成立(因为memset后的值比-0x3f3f3f3f大)
从而进入 dp[j]=max(dp[j],dp[j-s[i]]+f[i]); 的错误访问 (前提是if(s[i]<0成立))
或者你可以用不能达到预期目标的memset(本身性质所致 只能完美置0 -1 正的0x3f3f3f3f)
但是千万不要去招惹下标为负的情况
for(j=s[i];j<=200000+s[i];j++) ----> for(j=0;j<=200000+s[i];j++) 最下更新负到极限的0,最上更新到200000+s[i] Try to figure out it, just a little smartness.
啊,那么话说回来还是自己没有掌握好循环的边界情况啊……
所以,不要被事物的表面现象所迷惑
不是因为思路,不是因为头文件,不是因为memset承载能力有限,不是因为1<<30比0x3f3f3f3f吉利……
*/
/*精髓总结,前人会为你走出全部可能的路,如果你遇到了悬崖,那说明这个地方别人还没来过。并不是说别人来的时候有悬崖,而是因为别人来的时候别人的前人来过*/
/*假设你和你的族群是一群单体能力为登1级台阶的生物*/
/*你要上楼梯,起点是0,对于1的台阶,对于你不是悬崖,ok,1被征服,但是单靠自己不能到达2;后来的人面对3是悬崖,但是2不再是悬崖,在此次活动结束后会使3对于后来人不再是悬崖……*/
/*后来的人会发生变异,单体能力到达了n(n>=2),当然这代表能且仅能登上2阶台阶*/
/*漫长的演化,人们进化出了3 4 5 6 …… 种能力值(好吧这就要看测试数据了,代表01背包中的体积)*/
/*千奇百怪的能力使人们可以遵循不同路线,人心也在变化,有人开始收过路费,有的人却能给与过往人补给*/
/*所以需要规划出最优路线*/
/*而最优又不只有拿钱最多,花费最少这一种追求*/
/*参考头等舱的存在*/
/*这就要看出题者意图了*/
int main()
{
int i,j,k;
int ans=0;
while (cin>>N)
{
ans=-0x3f3f3f3f;
for(i=0;i<N;i++)
cin>>s[i]>>f[i];
for(i=0;i<=200000;i++)
dp[i]=-0x3f3f3f3f;
//memset(dp,-0x3f3f3f3f,sizeof(dp));
//for(i=0;i<10;i++)
//cout<<dp[i]<<" ";
dp[100000]=0;
for(i=0;i<N;i++)
{
if(s[i]<=0&&f[i]<=0) continue;
if(s[i]>0)
//因为s[i]>0,所以是从上一次小的得来,01背包的性质决定了应当从后往前更新 200000 (ok 如果有牛真的能冲到这里来) s[i](ok 边界嘛)
for(j=200000;j>=s[i];j--)
{
if(dp[j-s[i]]>-0x3f3f3f3f)
//要参与,需要挤掉一些份额
dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
}
else
//因为s[i]<0,所以如果你想要纳入当前项,就需要从 dp[j-s[i]] 即dp[j+|s[i]|] 得来
//那么既然需要使用比它大的值,为了避免重复应该先更新小的
for(j=s[i];j<=200000+s[i];j++)
{
if(dp[j-s[i]]>-0x3f3f3f3f)
dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
}
}
for(j=100000;j<=200000;j++)
{
if(dp[j]<0) continue;
if(j-100000+dp[j]>ans) ans=j-100000+dp[j];
}
cout<<ans<<endl;
}
return 0;
}
杂
//关于负数的思考例子 : -50000 100 -50000 500 -50000 200 -50000 200 痕迹,尽一切可能为后人留下能留下的足迹
//涉及 0x3f3f3f3f = 1061109567 INT_MAX = 2147483647 差不多是一半的关系, 前者*2 < 后者
//never mind‘前面 ’的都会在可能的地方留下自己痕迹,不必担心遗漏。
//不是不报,时候未到
来源:CSDN
作者:JXUFE_ACMer
链接:https://blog.csdn.net/JXUFE_ACMer/article/details/80365703