[无聊测试赛] 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;
}
posted @ 2020-03-21 12:31  pocafup  阅读(177)  评论(0编辑  收藏  举报