[无聊测试赛] T9 矩阵覆盖
这道题是神题,但是数据水,所以可以用一个很玄学的dp水过去
第一,这道题的数据没有k=4的数据. 第二,这题的所有数据中最优解的矩形要么是上下,要么左右(但其他数据会有不同的情况)
知道了这个以后,我们可以将 \(x\) 和 \(y\) 分别排序一次,然后用dp求解
这里的dp用三维. \(i,j,k\) 分别表示所用的矩阵数量,上一个矩阵的id和现在矩阵的id. \(dp[i][j][k]\) 里存的就是在这种情况所能拿到的最小值
开始我们先将使用一个矩阵的最小值统计出来,然后dp再慢慢找用第二个矩阵代替所能取到的值.
怎么求一个矩阵的覆盖面积呢? 答案就是底乘高,也就是从 \(j-k\) 之间的 \((max x - min x) * (max y - min y)\).这个操作可以用四个线段树来实现
而求第i个矩阵所覆盖的面积就是 \(max (dp[i][j][k], dp[i-1][j][u] + num[u+1][k])\). 其中 \(u \in{(1,j-1)}\) ,而 \(num[i][j]\) 等同于 \(dp[1][i][j]\).
#include <iostream>
#include <algorithm>
#include <cstring>
#include <math.h>
using namespace std;
#define pp pair<int,int>
#define f first
#define s second
#define mid (l+r)/2
#define lson (way<<1)
#define rson (way<<1)+1
const int MAXN = 55;
int n,k,dp[5][MAXN][MAXN],num[MAXN][MAXN],seg[MAXN*4][5],ans = 1e9;
pp pos[MAXN];
inline bool sorted_x(pp a, pp b){
return a.f<b.f || (a.f==b.f && a.s<b.s);
}
inline bool sorted_y(pp a, pp b){
return a.s<b.s || (a.s==b.s && a.f<b.f);
}//排序
inline int fxy(int way, int l, int r, int qlow, int qhigh, int type){//fxy就是query
if (qlow<=l && r<=qhigh) return seg[way][type];
if (qlow>r || qhigh<l) return 1e9*pow(-1,type);//pow是因为取最大值时如果发现答案不在范围内要输出最小值,反之输出最大
int le = fxy(lson,l,mid,qlow,qhigh,type);
int ri = fxy(rson,mid+1,r,qlow,qhigh,type);
return ((type%2) ? max(le,ri) : min(le,ri));//type%2的意思是type 1,3 为求最大值, 2,4为求最小值. %2找现在的状态
}
inline void make_tree(int type, int way, int l, int r){//建树
if (l==r){
seg[way][type] = (type>2) ? pos[l].s : pos[l].f;
return;
}
make_tree(type,lson,l,mid);
make_tree(type,rson,mid+1,r);
seg[way][type] = (type%2) ? max(seg[lson][type],seg[rson][type]) : min(seg[lson][type],seg[rson][type]);
}
inline void find_ans(){
memset(dp,0x3f3f,sizeof(dp));
for (int i=1;i<=4;i++) make_tree(i,1,1,n);//建四棵树
for (int i=1;i<=n;i++){
for (int j=i+1;j<=n;j++){
num[i][j] = dp[1][i][j] = (fxy(1,1,n,i,j,1)-fxy(1,1,n,i,j,2)) * (fxy(1,1,n,i,j,3)-fxy(1,1,n,i,j,4));//一个矩形
}
}
// for (int i=1;i<=n*4;i++) cout << seg[i][4] << " ";
for(int i=2;i<=k;i++){
for(int j=1;j<=n;j++){
for(int l=j+1;l<=n;l++){
for(int u=j;u<l;u++)
dp[i][j][l]=min(dp[i][j][l],dp[i-1][j][u]+num[u+1][l]);//转移
}
}
}
ans = min(ans,dp[k][1][n]);//答案就是用了k个矩形从1覆盖到n的最小值
}
int main(){
cin >> n>>k;
for (int i=1;i<=n;i++) cin >> pos[i].f >> pos[i].s;
sort(pos+1,pos+n+1,sorted_x);
find_ans();
sort(pos+1,pos+n+1,sorted_y);
find_ans();
cout << ans;
}