题意:n头牛,每头牛有两个属性值,smart值s,fun值f,求选出其中的m头牛,使得s和f的总和最大,并保证各自的s和以及f和都大于0.
分析:
怎么保证TS和TF都是>=0的条件下使得TS+TF最大?
先保证TS和TF都是>=0,然后遍历一边取TS+TF的最大值就ok了。
转化问题,求s和为某个固定值时候最大的f和值,然后遍历这些所有的s和以及对应的f和值,求出总和总和最大的那个。
那么这样就是一个0-1背包问题,可以把s值理解为代价,f值理解为价值
dp[c]代表s和为c时候,f和能取到的最大值。
状态转移方程: dp[c]=max{dp[c], dp[c-s[i]+f[i]}
注意:
因为s有小于0的情况,s的范围在[-1000,1000]之间,最多有100头牛,所以和的范围在[-100000,100000]之间,为了避免负数作为下标,s和都加上100000,整体的范围就变为[0,200000]。
当s[i]>0的时候,dp[c]的计算顺序从大往小计算,因为在计算第i头牛的最优f和时,dp[c-s[i]]是前i-1头牛的最优f和,而这个值在dp[c]的左侧,所以我们从右往左计算,这样就可以利用前i-1的最优解,不会覆盖。
同理当s[i]<0的时候,dp[c]的计算顺序从小往大计算,因为dp[c-s[i]]这个时候dp[c]的右侧了。
1 #include<cstdio> 2 #include<cstring> 3 4 const int maxn=200000+10; 5 const int inf=0x3f3f3f3f; 6 7 int dp[maxn]; 8 int s[105]; 9 int f[105]; 10 11 int max(int a,int b) 12 { 13 return a>b?a:b; 14 } 15 16 int main() 17 { 18 int n; 19 while(~scanf("%d",&n)) 20 { 21 int up=100000,down=100000; 22 for(int i=1;i<=n;i++) 23 { 24 scanf("%d%d",&s[i],&f[i]); 25 if(s[i]>0) 26 up+=s[i]; 27 else 28 down+=s[i]; 29 } 30 31 for(int i=down;i<=up;i++) 32 dp[i]=-inf; 33 dp[100000]=0; 34 for(int i=1;i<=n;i++) 35 { 36 if(s[i]<=0&&f[i]<=0) 37 continue; 38 if(s[i]>0) 39 { 40 for(int j=up;j>=down+s[i];j--) 41 { 42 dp[j]=max(dp[j],dp[j-s[i]]+f[i]); 43 } 44 } 45 else 46 { 47 for(int j=down;j<=up+s[i];j++) 48 { 49 dp[j]=max(dp[j],dp[j-s[i]]+f[i]); 50 } 51 } 52 } 53 int ans=0; 54 for(int i=100000;i<=up;i++) 55 { 56 if(dp[i]>=0) 57 ans=max(ans,i+dp[i]-100000); 58 } 59 printf("%d ",ans); 60 } 61 return 0; 62 }