牛客多校第十场J-Wood Processing

题目:
In the wood industry, it is very common to make big wood boards by combining planks. To combine several planks into boards, the carpenter may cut some of the planks horizontally and discard one of the two parts, such that the heights of all planks are equal. Then, the planks are joined together, forming a big wood board. The height of the board is the common height of the planks, and the width of the board is the sum of the widths of the planks.

However, cutting planks may result in huge wastes. The problem is, given n planks, determine the minimum total wasted area of planks to make k boards from these planks. You may freely reorder and combine these planks. Note that the mechanical properties of a plank are anisotropic, so you can’t rotate the planks. Also, all planks must be used; you cannot discard any whole plank.
数据范围:n, k (1 ≤ n ≤ 5000,1 ≤ k ≤ 2000,k ≤ n), denoting the number of planks given and the number of boards to make. w, h(1 ≤ w,h ≤ 10⁷), denote the width and the height of a given plank.
样例:

输入
3 2
3 3
4 7
2 5
输出
4

样例解释:将高度为3的和高度为5的合并为第一个大木板,浪费面积为4,剩下一个高度为7的作为一个大木板,所以最小浪费面积为4.
题意:
有n块木板,要合并成k块大木板,每次合并,高度必须相同(木板可以切割,但是不能旋转),长度必须是合成该大木板的所有木板的长度合,问最少的浪费面积是多少。
做法:
二分优化dp
思路:
可以判断,每次合并的木板必定是长度连续的一段,所以先把木板从高到低排序,每次切割连续的一段作为一个大木板。定义dp[i][j]为前i刀切割,切割前j块木板的最小浪费面积。那么可以得出转移方程dp[i][j] = max_k(dp[i-1][k] +sum[j]-sum[k]+a[j].h(len[j] - len[k])* ,其中sum数组代表排序之后木板的面积前缀和,len数组代表排序之后木板的长度前缀合,每块木板用结构体存储。由于上述转移拥有决策单调性,可以通过经典的分治优化dp进行优化。

代码:

#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5005;
const ll inf = 0x3f3f3f3f3f3f3f3f;

ll dp[2005][maxn],sum[maxn],len[maxn],q[maxn];

struct node
{
    ll w,h;
};
node a[maxn];

bool cmp(node x,node y)
{
    return x.h > y.h;
}

ll jian(int s,int t)
{
	return sum[t]-sum[s-1]-a[t].h*(len[t] - len[s-1]);
}

void fun(int i,int l,int r,int ql,int qr)
{
	if(l > r) return ;
	ll minn = inf;
	int kk = (l+r)/2;
	int m;
	for(int j = ql;j < min(kk,qr+1);j++){
		if(minn > dp[i-1][j] + jian(j+1,kk)){
			minn = dp[i-1][j] + jian(j+1,kk);
			m = j;
		}
	}
	dp[i][kk] = minn;
	fun(i,l,kk-1,ql,m);
	fun(i,kk+1,r,m,qr);
}

int main()
{
	int n,k;
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i++){
        scanf("%lld%lld",&a[i].w,&a[i].h);
    }
    sort(a+1,a+n+1,cmp);//从大到小排序 
    sum[0]=0,len[0]=0;
    for(int i = 1;i <= n;i++){
    	sum[i] = sum[i-1]+a[i].w*a[i].h;//木板面积的前缀和 
    	len[i] = len[i-1]+a[i].w;//长度的前缀和 
    	dp[1][i] = sum[i] - a[i].h*len[i];//表示1刀割到第j个木板割掉的面积 
	}
	for(int i = 2;i <= k;i++){
		fun(i,i,n,i-1,n-1);//二分最优解
	}
	printf("%lld\n",dp[k][n]);
	return 0;
}

还有其他的优化方法也可以

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值