HDU 5543 Pick The Sticks:01背包变种

时间:2021-10-28 12:58:20

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5543

题意:

  给你N个金条和一张长度为L的桌子。每个金条长度为a[i],价值为w[i]。金条只能在桌子上横着摆一排,并且只要金条的重心(中心)在桌子上,就可以放。问你在桌子上能够摆的金条的最大总价值。

题解:

  首先表示状态:

    考虑到第i个金条,在这之前已经占用了j的长度,在k个端点摆了金条。即:dp[i][j][k]

  如何转移(逆推):

    对于第i个金条,有三种决策:摆在桌子*、摆在桌子端点处、不摆。

    (1)摆在*:dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a[i]][k])

    (2)摆在端点:dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a[i]/2][k-1])

    (3)不摆:  dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k])

  求dp:枚举i,j,k即可。

  注:(1)由于空间限制,dp[i][j][k]去掉第一维[i],枚举j时从大到小枚举。

    (2)由于计算金条长度的一半时会出现浮点数,所以在读入时就将所有a[i]*=2,并且L*=2。

AC Code:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAX_N 1005
#define MAX_L 4005
#define MAX_K 5 using namespace std; int n,l,t;
long long ans;
long long a[MAX_N];
long long w[MAX_N];
long long dp[MAX_L][MAX_K]; void read()
{
ans=;
cin>>n>>l;
l*=;
for(int i=;i<n;i++)
{
cin>>a[i]>>w[i];
a[i]*=;
ans=max(ans,w[i]);
}
} void solve()
{
memset(dp,,sizeof(dp));
for(int i=;i<n;i++)
{
for(int j=l;j>=a[i]/;j--)
{
for(int k=;k<;k++)
{
if(j-a[i]>=) dp[j][k]=max(dp[j][k],dp[j-a[i]][k]+w[i]);
if(k->=) dp[j][k]=max(dp[j][k],dp[j-a[i]/][k-]+w[i]);
ans=max(ans,dp[j][k]);
}
}
}
} void print()
{
cout<<ans<<endl;
} int main()
{
cin>>t;
for(int cas=;cas<=t;cas++)
{
cout<<"Case #"<<cas<<": ";
read();
solve();
print();
}
}