牛客第三场 J Wood Processing —— 斜率dp

本文介绍了一种利用动态规划和斜率优化技术解决木块分组以最小化浪费面积的问题。通过将木块按高度排序并使用DP算法,结合上凸包维护最优解,实现了O(nk)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n w ∗ h w*h wh 的木块,分成 k k k
    每组木块全部切成当组木块的最低高度
    求最少浪费面积

解题思路:

    首先按高度从高到低排序
     d p [ i ] [ k ] dp[i][k] dp[i][k] 表示到第 i i i 块,分为 k k k 组的最大保留面积
    答案即为 s u m − d p [ n ] [ k ] sum - dp[n][k] sumdp[n][k],设 p r e pre pre 为宽度前缀和
     d p [ i ] [ k ] = m a x ( d p [ j ] [ k − 1 ] + ( p r e [ i ] − p r e [ j ] ) × h [ i ] ) dp[i][k] = max(dp[j][k-1] + (pre[i] - pre[j]) \times h[i]) dp[i][k]=max(dp[j][k1]+(pre[i]pre[j])×h[i])
    时间复杂度: O ( n 2 k ) O(n^2k) O(n2k)

    考虑斜率优化,设 j 1 < j 2 < i j_1<j_2<i j1<j2<i
    当 d p [ j 1 ] [ k − 1 ] + ( p r e [ i ] − p r e [ j 1 ] ) × h [ i ] dp[j_1][k-1] + (pre[i] - pre[j_1]) \times h[i] dp[j1][k1]+(pre[i]pre[j1])×h[i]   < <   d p [ j 2 ] [ k − 1 ] + ( p r e [ i ] − p r e [ j 2 ] ) × h [ i ] dp[j_2][k-1] + (pre[i] - pre[j_2]) \times h[i] dp[j2][k1]+(pre[i]pre[j2])×h[i]
     j 2 j_2 j2 j 1 j_1 j1 更优,则 j 1 j_1 j1 可以从决策集中删去

    转化为   d p [ j 2 ] [ k − 1 ] − d p [ j 1 ] [ k − 1 ] p r e [ j 2 ] − p r e [ j 1 ] \frac{dp[j_2][k-1] - dp[j_1][k-1]}{pre[j_2]-pre[j_1]} pre[j2]pre[j1]dp[j2][k1]dp[j1][k1] > >   h [ i ] h[i] h[i]

    符号为大于,维护上凸包,斜率递减,由于 h [ i ] h[i] h[i] 递减,在队首操作
    时间复杂度: O ( n k ) O(nk) O(nk)

    参考博客:暮冥

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
using pii = pair <ll,int>;
const int maxn = 5e3 + 5;
int n, k, q[maxn];
ll sum, pre[maxn];
ll dp[maxn][2019];

struct node{
	int w, h;
	bool operator < (const node &A){
		return h > A.h;
	}
} a[maxn];

long double slope(int x, int y, int p){
	long double t = dp[x][p-1] - dp[y][p-1];
	return t / (pre[x] - pre[y]);
}

int main() {
	scanf("%d%d", &n, &k);
	for(int i=1; i<=n; i++) scanf("%d%d", &a[i].w, &a[i].h);
	sort(a+1, a+n+1);
	for(int i=1; i<=n; i++){
		pre[i] = pre[i-1] + a[i].w;
		sum += 1ll * a[i].h * a[i].w;
	}
	for(int p=1; p<=k; p++){
		int l = 0, r = 0;
		for(int i=1; i<=n; i++){
			while(l<r && slope(q[l], q[l+1], p) >= a[i].h) l++;
			dp[i][p] = dp[q[l]][p-1] + 1ll * (pre[i] - pre[q[l]]) * a[i].h;
			while(l<r && slope(q[r], q[r-1], p) <= slope(q[r], i, p)) r--;
			q[++r] = i;
		}
	}
	printf("%lld\n", sum - dp[n][k]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值