Pick The Sticks(题目链接)
这道题其实是01背包的变形。对,掐头去尾就是一个01背包
所以这道题的处理方法其实和01背包很像!
但是难点还在。就是如何处理头和尾
我第一次是列举出所有的头,特殊处理
wa了。自己还是没有理解背包。犯了同样的错误
对于头和尾,只有两个特殊情况,那么和上面链接的题目一样
特殊处理,01背包单独跑
对于每一个物品,他都可能是头和尾,那么我把他当头和尾(即用的时候用长度的一半)
来跑一边01背包。将所有情况找一下最优解
这样就ok了
dp用了三层,第一层的dp记录的是假设金条不可以出界的情况(即完完全全的01背包)
下面的第二层dp记录的是用当前的物品当做头或者尾的情况(如果不能做头和尾,那么保留原数)
第三层dp记录了有两个物品当头或者尾的情况(这两个物品)
三次背包同时跑保证了一个物品不会被两次当做头或者尾。如果分开跑
第二层跑完了,即已经加上了一个头或尾,第三层dp就会多加
而且跑第三层dp需要第二层dp的值!而我们先跑了第二层dp就会把值覆盖
下面代码
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#define ll long long
using namespace std;
int u[10005];
ll v[10005];
ll dp[5005][5];
int main()
{
int t,step=1;
scanf("%d",&t);
while(t--)
{
int n,L;
scanf("%d%d",&n,&L);
L*=2;
memset(dp,0,sizeof(dp));
ll ans=0;
for(int i=1; i<=n; i++)
{
scanf("%d%lld",&u[i],&v[i]);
u[i]=2*u[i];
if(v[i]>ans)
ans=v[i];
}
for(int i=1; i<=n; i++)
{
for(int j=L; j>=0; j--)
{
for(int l=0; l<=2; l++)
{
if(j>=u[i])
dp[j][l]=max(dp[j][l],dp[j-u[i]][l]+v[i]);
if(l&&j>=u[i]/2)
dp[j][l]=max(dp[j][l],dp[j-u[i]/2][l-1]+v[i]);
}
}
}
for(int i=0;i<=L;i++)
{
for(int j=0;j<=2;j++)
{
if(dp[i][j]>ans)
ans=dp[i][j];
}
}
printf("Case #%d: %lld\n",step++,ans);
}
}