题目描述:
给出一个序列cow的属性值。对应cow的属性有smartness 和 funess。要求:在smartess和funess加和都大于0的条件下,sum of 这两个值最大的一个cow序列求出的sum值。
解题思路:
对应于cow就是选或者不选两种可能,所以是0/1背包问题。对于这个问题,有两列值,不同于标准里的只有一个容量限制。等于我要求的是,按照smart为下标的数组,挨个加入一只cow,更新对应这个smart值的funess值最大可能是多少,最后做完背包更新,求大于0的那个smart数组的加和就可以了。
那么在那个过程中,就要判断更新是正向还是反向进行(我们知道0/1里都是反向进行的,本质是怕更新重复。这里有负值,所以需要注意更新方向。)
代码:
#include<stdio.h>
#include <stdlib.h>
#define MIN -10000001
#define N 100001
int s[1001], f[1001];
int f1[N], f2[N], n;
main(){
int i, v, min_st, max_st, tmp, max, j;
scanf("%d",&n);
min_st = 0;
max_st = 0;
for(i=1;i<=n;i++){
scanf("%d %d",&s[i], &f[i]);
if(s[i]<0)
min_st += s[i];
else
max_st += s[i];
}
//printf("min_st:%d, max_st:%d\n",min_st, max_st);
tmp = (0-min_st) > max_st? (0-min_st) : max_st;
for(i=0;i<=tmp;i++){
f2[i] = MIN;
f1[i] = MIN;
}
for(i=1;i<=n;i++){
if(s[i]>=0){ //反向更新
for(v=max_st; v>=min_st+s[i]; v--){
if(v>=0 && v-s[i]>=0 && f2[v-s[i]]!=MIN)
f2[v] = f2[v-s[i]] + f[i] > f2[v] ? f2[v-s[i]] + f[i] : f2[v] ;
if(v>=0 && v-s[i]<0 && f1[s[i]-v]!=MIN)
f2[v] = f1[s[i]-v] + f[i] > f2[v] ? f1[s[i]-v] + f[i] : f2[v] ;
if(v<0 && f1[s[i]-v]!=MIN)
f1[0-v] = f1[s[i]-v] + f[i] > f1[0-v] ? f1[s[i]-v] + f[i] : f1[0-v] ;
}
if(f2[s[i]]<f[i])
f2[s[i]] = f[i];
}else{ //正向更新
for(v=min_st; v<=max_st+s[i]; v++){
if(v<0 && v-s[i]>=0 && f2[v-s[i]]!=MIN)
f1[0-v] = f2[v-s[i]] + f[i] > f1[0-v] ? f2[v-s[i]] + f[i] : f1[0-v] ;
if(v<0 && v-s[i]<0 && f1[s[i]-v]!=MIN)
f1[0-v] = f1[s[i]-v] + f[i] > f1[0-v] ? f1[s[i]-v] + f[i] : f1[0-v] ;
if(v>=0 && f2[v-s[i]]!=MIN)
f2[v] = f2[v-s[i]] + f[i] > f2[v] ? f2[v-s[i]] + f[i] : f2[v] ;
}
if(f1[0-s[i]]<f[i])
f1[0-s[i]] = f[i];
}
// printf("加完第%d组[%d,%d]后,数组为:\n",i,s[i],f[i]);
// printf("f1:\n");
// for(j=1;j<=(0-min_st);j++)
// printf("%d\t",f1[j]);
// printf("\nf2:\n");
// for(j=0;j<=max_st;j++)
// printf("%d\t",f2[j]);
// printf("\n");
}
max = 0;
for(i=0;i<=max_st;i++)
if( f2[i]>=0 && i+f2[i] > max)
max = i + f2[i];
printf("%d\n",max);
system("pause");
return 0;
}