题目:hdu5544
题意:给一个长度为L的平台,和n根金条的长度与价值,金条只能在平台上摆一长条,且每根金条的重心不能超出平台边缘。
解答:01背包dp加一维表示当前物品与之前物品已经占用了多少个边缘。
dp[k][i][v] :
第一维:
k=0,1,2
。
0
代表没有半截的情况,
1
代表有
1
个半截的情况,
2
代表有
2
个半截的情况。
第二维:前
i
物品。
第三位:占了
v
容量。
那么 DP 转移方程为:
dp[0][i][v]=max(dp[0][i−1][v],dp[0][i−1][v−c[i]]+w[i])dp[1][i][v]=max(dp[1][i−1][v],dp[1][i−1][v−c[i]]+w[i],dp[0][i−1][v−c[i]2]+w[i])dp[2][i][v]=max(dp[2][i−1][v],dp[2][i−1][v−c[i]]+w[i],dp[1][i−1][v−c[i]2]+w[i])
小
trick
:
1
:对于容量
c[i]2
不能被
2
整除时,要把它变成整数,所以初始化的时候
c[i]<<=1
。
2
:只放
1
个物品的时候,无论背包容量有多大,任何物品都能放进去。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int n,L;
const int MAXN = 4040;
struct Stick
{
int l,v;
};
Stick s[MAXN];
ll dp[MAXN][3];
int main()
{
int T;
scanf("%d",&T);
for(int t = 1;t <= T;t++)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&L);
L *= 2;
ll ans = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&s[i].l,&s[i].v);
s[i].l *= 2;
ans = max(ans,(ll)s[i].v);
}
for(int i = 1;i <= n;i++)
for(int j = L;j>=0;j--)
{
for(int k = 0;k < 3;k++)
{
if(j >= s[i].l)
dp[j][k] = max(dp[j][k],dp[j-s[i].l][k]+s[i].v);///注意:不管k是几都可以放在中间
if(k)
if(j >= (s[i].l/2))
dp[j][k] = max(dp[j][k],dp[j-(s[i].l/2)][k-1]+s[i].v);
}
}
ans = max(ans,dp[L][2]);
printf("Case #%d: %lld\n",t,ans);
}
return 0;
}