之前写得贪心是有问题的。
贪心的策略并不行,只是能过测试点而已。
因为如果你的体积刚好只能放n个体积为3的,然后有一个体积为1的,性价比高于这些体积为三的,但是价值要低一些,那么贪心的策略会选择体积为1的。总价值会低一些。
看了官方题解之后会写正解了,详细说一下。
先贴一下原文。
There are lots of different solutions for this problem.
We can iterate on the number of 3-elements we will take (in this editorial k-element is a souvenir with weight k). When fixing the number of 3-elements (let it be x), we want to know the best possible answer for the weight m - 3x, while taking into account only 1-elements and 2-elements.
To answer these queries, we can precalculate the values dp[w] — triples (cost, cnt1, cnt2), where cost is the best possible answer for the weight w, and cnt1 and cnt2 is the number of 1-elements and 2-elements we are taking to get this answer. Of course, dp[0] = (0, 0, 0), and we can update dp[i + 1] and dp[i + 2] using value of dp[i]. After precalculating dp[w] for each possible w we can iterate on the number of 3-elements.
There are also several binary/ternary search solutions.
首先很容易观察到的是,对于相同质量的物品,我肯定会先选择价值更大的物品。
然后考虑这样一个问题,我们需要选择几个价值为3的物品?如果我选择了x个重量为3的物品,那么总价值就是前x个重量3的物品的价值和,加上重量不超过m-3*x的重量为1和2组成的物品的价值的最大值。
所以用动态规划预处理一下,我只选重量为1和2的物品,质量小于w时的质量最大值。
dp[w]表示一个三元组,表示了重量为w的情况下,最大的价值,以及重量为1和2分别选择的数量。dp[w]可以通过dp[w-1]和dp[w-2]处理出来。
具体可以看代码,感觉逻辑还是比较清楚地。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define _ sync_with_stdio(false)
const int MAXN=300000+10;
const int INF=0x7fffffff;
typedef long long ll;
struct{
ll v;
ll s1,s2;
}dp[MAXN];
ll a[4][MAXN];//每种数量物品的价值
ll s[4][MAXN];//价值前缀和
int num[4];//每种重量物品的数量
int cmp(int x,int y){return x>y;}
int main() {
int n,m;
scanf("%d%d",&n,&m);
num[1]=num[2]=num[3]=0;
for(int i=1;i<=n;i++){
ll w,c;
scanf("%lld%lld",&w,&c);
a[w][++num[w]]=c;
}
for(int i=1;i<=3;i++){
sort(a[i]+1,a[i]+num[i]+1,cmp);
for(int j=1;j<=num[i];j++){
s[i][j]=s[i][j-1]+a[i][j];
}
}
dp[0].v=dp[0].s1=dp[0].s2=0;
for(int i=1;i<=m;i++){
dp[i]=dp[i-1];
if(dp[i-1].v+a[1][dp[i-1].s1+1]>dp[i].v){
dp[i].v=dp[i-1].v+a[1][dp[i-1].s1+1];
dp[i].s1=dp[i-1].s1+1;
dp[i].s2=dp[i-1].s2;
}
if(i>=2&&dp[i-2].v+a[2][dp[i-2].s2+1]>dp[i].v){
dp[i].v=dp[i-2].v+a[2][dp[i-2].s2+1];
dp[i].s1=dp[i-2].s1;
dp[i].s2=dp[i-2].s2+1;
}
}
ll res=0;
for(int i=0;i<=num[3];i++){
if(m>=i*3)
res=max(res,s[3][i]+dp[m-3*i].v);
}
printf("%I64d\n",res);
}
下面这个是错误的解,可以直接忽略。
看起来就像是一个01背包,不过每个物品的体积只有1,2,3三种情况,但是总体积可能很大。
那么就先根据性价比排个序,贪心地缩小体积的范围。然后再用01背包求解。那个范围可以使1000,或者100,或者50都行,最小是多少得看数据...
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
using namespace std;
#define _ ios::sync_with_stdio(false)
const int MAXN = 100010;
const int INF=0x7fffffff;
int n,m;
struct node{
long long w,c;
long long x;
}a[MAXN];
int cmp(node p,node q){
return p.c>q.c;
}
long long res=0;
long long f[MAXN];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].w,&a[i].x);
a[i].c=6*a[i].x/a[i].w;
}
sort(a+1,a+n+1,cmp);
int i=1;
while(m>1000&&i<=n){
res+=a[i].x;
m-=a[i].w;
i++;
}
while(i<=n){
for(int v=m;v>=a[i].w;v--){
f[v]=max(f[v],f[v-a[i].w]+a[i].x);
}
i++;
}
printf("%lld\n",res+f[m]);
}
额...这个代码有问题,贪心的策略好像并不行,只是能过测试点而已。
因为如果你的体积刚好只能放n个体积为3的,然后有一个体积为1的,性价比高于这些体积为三的,但是价值要低一些,那么贪心的策略会选择体积为1的。总价值会低一些。