[DP 01背包+差值]P1651 塔
思路
令
f
[
i
]
[
j
]
f[i][j]
f[i][j]为从前i个木块中选,差值为j时,较高塔的高度
那么有如下状态转移
不选第i个
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j]
选第i个放较高的塔
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
a
[
i
]
]
+
a
[
i
]
f[i][j]=f[i-1][j-a[i]]+a[i]
f[i][j]=f[i−1][j−a[i]]+a[i]
选第i个放较低的塔,放了后还是比高的低
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
+
a
[
i
]
]
f[i][j]=f[i-1][j+a[i]]
f[i][j]=f[i−1][j+a[i]]
选第i个放较低的塔,放了后比高的高
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
a
[
i
]
−
j
]
+
j
f[i][j]=f[i-1][a[i]-j]+j
f[i][j]=f[i−1][a[i]−j]+j
思考状态转移的时候有几个需要注意的点
①我们遍历的j是,已经放完当前木块后的j,也就是放完后的差值
②然后DP写的时候是反过来写得如果写得不顺的话你这样写
例如:选第i个放较低的塔,放了后比高的高
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
t
]
+
j
f[i][j]=f[i-1][t]+j
f[i][j]=f[i−1][t]+j
f
[
i
−
1
]
[
t
]
是
原
本
较
高
塔
的
高
度
,
也
是
放
完
后
较
低
塔
的
高
度
,
j
是
放
完
后
塔
的
差
值
f[i-1][t]是原本较高塔的高度,也是放完后较低塔的高度,j是放完后塔的差值
f[i−1][t]是原本较高塔的高度,也是放完后较低塔的高度,j是放完后塔的差值
列出已知就可以得到t和h1
初始化方面:需要做转移的封闭,因为求最大值,所以搞个负的封闭,然后f[0][0]=f[1][0]=0
转移的条件:差值不能为负,也不能超过sum
代码
// Problem: P1651 塔
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1651
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// FishingRod
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY
f[1][0]=
*/
int T;
const int N=55,M=5e5+10;
int f[N][M],a[N];
void solve(int C)
{
//NEW DATA CLEAN
//NOTE!!!
int n;cin>>n;
LL sum=0;
for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i];
memset(f,0xcf,sizeof f);//转移封闭
f[0][0]=f[1][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
int &x=f[i][j];
x=max(x,f[i-1][j]);
if(j+a[i]<=sum)x=max(x,f[i-1][j+a[i]]);//放矮塔,没超过高的
if(a[i]>j)x=max(x,f[i-1][a[i]-j]+j);//放矮塔,超过高的
else x=max(x,f[i-1][j-a[i]]+a[i]);//放高塔
}
}
if(f[n][0]==0)cout<<-1<<endl;
else cout<<f[n][0]<<endl;
}
int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}