HDU 3669 Cross the Wall
题目大意
给定 \(N\) 个矩形的宽和高, \((h_1, w_1), (h_2, w_2), \dots, (h_n w_n)\) . 现需要确定 \(k\) (\(k \le K\), \(K\) 给定) 个矩形 \((H_i, W_i)\) , 使得 \(\forall i \ (1\le i\le N), \exists j,\ s.t.\ H_j \ge h_i\) 且 \(W_j \ge w_i\), 此时称这后 \(k\) 个矩形覆盖前 \(N\) 个矩形, 代价为 \(\sum_{1\le i \le k} H_i W_i\) , 即 \(k\) 个矩形的面积之和. 求覆盖这 \(N\) 个矩形的最小代价.
Solution
DP方程
对 \(N\) 个矩形按 (宽, 高) 双关键字从小到大排序. 按此顺序构造一个高严格递减的矩形队列, 只考虑这个队列里的矩形, 记队列里的剩下的矩形数目为 \(n, \ (n \le N)\) .
\(dp[i][j]\) : 用 \(j\) 个矩形覆盖队列中前 \(i\) 个矩形的最小代价.
边界条件: \(dp[0][0]=0\)
斜率优化
固定 \(j\) , 用斜率优化 \(i\) 这一维的决策.
关于斜率优化的细致讨论见 this post.
写出斜率不等式:
其中
维护一个相邻点斜率严格递增的点的队列, 由于 \(f(i)\) 单调递增 , 在维护单调队列的基础上, 计算 \(dp[i][j]\) 之前, 若队首两点 \(i_1, i_2\) 满足 \(g(i_1, i_2) \le f(i)\) , 就将队首元素出队, 重复该过程. 将队首元素代入 \(\ref{dp_eq}\) 式右边计算 \(dp[i][j]\) 的值.
Implementation
#include <bits/stdc++.h>
using namespace std;
const int N=5e4+5, K=105;
using ll=long long;
ll dp[N][K];
// dp[i][j]=min()
int n, k;
ll w[N], h[N];
bool cmp(int x, int y){
return w[x]<w[y] || w[x]==w[y] && h[x]<h[y]; // error-prone
}
int idx[N];
int q[N], t; // q[0]=0
void prep(){
for(int i=1; i<=n; i++)
idx[i]=i;
sort(idx+1, idx+n+1, cmp);
t=0;
// q[++tail]=idx[1];
for(int i=1; i<=n; i++){
while(t && h[q[t]]<=h[idx[i]])
--t;
q[++t]=idx[i];
}
}
// x < y: index of q
ll nu(int i, int x, int y){
return dp[y][i]-dp[x][i];
}
ll de(int x, int y){
return h[q[x+1]]-h[q[y+1]];
}
// the first dimension of dp[i][j] and q[i] are associated.
int que[N], head, tail;
int main(){
h[0]=1e6+5;
for(; cin>>n>>k; ){
for(int i=1; i<=n; i++)
scanf("%lld%lld", w+i, h+i);
prep();
for(int j=0; j<=k; j++)
dp[0][j]=0;
for(int i=1; i<=t; i++)
dp[i][0]=1e12+5; //error-prone
// p_i: (h[q[i+1]], dp[i])
for(int j=1; j<=k; j++){ // j: number of rectangles.
head=tail=0; // index of que
// 1. idx of q stored 2. 0-indexed
que[tail++]=0;
for(int i=1; i<=t; i++){ // i: index of q
while(tail-head>=2 &&
nu(j-1, que[head], que[head+1]) <= de(que[head], que[head+1])*w[q[i]])
++head;
dp[i][j]=dp[que[head]][j-1]+h[q[que[head]+1]]*w[q[i]];
// printf("(%d,%d):%lld\n", i, j, dp[i][j]);
while(tail-head>=2 &&
nu(j-1, que[tail-2], que[tail-1])*de(que[tail-1], i)
>=nu(j-1, que[tail-1], i)*de(que[tail-2], que[tail-1]))
--tail;
que[tail++]=i; //error-prone
}
}
ll res=LLONG_MAX;
for(int j=1; j<=k; j++)
res=min(res, dp[t][j]);
assert(res>0);
cout<<res<<endl;
}
return 0;
}
Pitfalls
如何将已经能被其他矩形覆盖的矩形剔除.