题目:
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;
}
还有其他的优化方法也可以