刷题记录:洛谷P8806 [蓝桥杯 2022 国 B] 搬砖 条件排序dp
传送门:洛谷
题目描述
这天,小明在搬砖。
他一共有 nnn 块砖,他发现第 iii 砖的重量为 wiw_{i}wi,价值为 viv_{i}vi。他突然想从这些砖中选一些出来从下到上堆成一座塔,并且对于塔中的每一块砖来说,它上面所有砖的重量和不能超过它自身的价值。
他想知道这样堆成的塔的总价值(即塔中所有砖块的价值和)最大是多少。
输入:
5
4 4
1 1
5 2
5 5
4 3
输出:
10
一道比较典的题目,故写题解记录一下
对于这道题如果没有想到对数列进行一个人为排序的,那么这道题会感觉没有什么头绪,但是如果提前了解过这种题型,那么这种题目写起来也就那么一回事
对于这道题,我们感觉最棘手的肯定是如何满足题目中保证上面所有砖的重量和不能超过它自身的价值这个条件.
我们考虑是否存在一个顺序,当我们对原数列按这种顺序进行排序之后的所有从前往后的子序列都满足我们的上述条件
我们假设存在一堆满足题意的砖块,其中有i,ji,ji,j两个砖块,并且iii在jjj的下方,我们设在(i,j)(i,j)(i,j)之间的所有砖块的总重量为w[1]w[1]w[1],设堆在jjj上面的所有砖块的质量为w[2]w[2]w[2].那么此时显然满足这样的两个条件:
v[i]>w[1]+w[2]+w[j]v[i]>w[1]+w[2]+w[j]v[i]>w[1]+w[2]+w[j]v[j]>w[2]v[j]>w[2]v[j]>w[2]
那么假设此时i,ji,ji,j可以进行上下交换,我们又需要满足什么条件呢
v[j]>w[1]+w[2]+w[i]v[j]>w[1]+w[2]+w[i]v[j]>w[1]+w[2]+w[i]v[i]>w[2]v[i]>w[2]v[i]>w[2]
那么此时我们只需要v[j]−w[i]>v[i]−w[j]v[j]-w[i]>v[i]-w[j]v[j]−w[i]>v[i]−w[j],因为v[i]−w[j]>w[1]+w[2]v[i]-w[j]>w[1]+w[2]v[i]−w[j]>w[1]+w[2],就会有v[j]−w[i]>w[1]+w[2]v[j]-w[i]>w[1]+w[2]v[j]−w[i]>w[1]+w[2]
这说明了什么东西呢,说明只要我们的<i,j>满足v[j]−w[i]>v[i]−w[j]v[j]-w[i]>v[i]-w[j]v[j]−w[i]>v[i]−w[j],对于所有的合法的堆法,只要我们的iii在jjj的下方,此时我们就可以将i,ji,ji,j进行交换
进一步来说,只要我们对原式子进行从小到大排序,那么对于最终的所有合法的堆法来说,肯定是我们的这个序列的一个子序列,所以我们只要按照这个顺序进行堆砖块,最后的顺序就是满足提题意的
然后接下来就是一个01背包取数问题了,我们设dp[i][j]dp[i][j]dp[i][j]为前iii个物品在可容量为jjj的时候能放下的最大价值,那么对于我们后面的i+1i+1i+1来说,只要此时我们的v[i+1]>jv[i+1]>jv[i+1]>j的情况,我们都都可以进行转移
当然我们也可以进行滚动数组优化
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {ll x=0,w=1;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int n;
struct Zhuankuai{int w,v;bool operator < (const Zhuankuai &rhs) const {return w+v<rhs.w+rhs.v;}
}zk[maxn];
int dp[maxn];
int main() {n=read();for(int i=1;i<=n;i++) {zk[i].w=read();zk[i].v=read();}sort(zk+1,zk+n+1);for(int i=1;i<=n;i++) {for(int j=20000;j>=0;j--) {if(zk[i].v>=j-zk[i].w&&j-zk[i].w>=0)dp[j]=max(dp[j],dp[j-zk[i].w]+zk[i].v);else {continue;}}}int ans=-int_INF;for(int i=0;i<=20000;i++) {ans=max(ans,dp[i]);}cout<<ans<<endl;return 0;
}