1. 程式人生 > >【XSY2693】景中人 區間DP

【XSY2693】景中人 區間DP

inline AI 次數 set stdin %d names sin pac

題目描述

  平面上有\(n\)個點,你要用一些矩形覆蓋這些點,要求:

  • 每個矩形的下邊界為\(y=0\)
  • 每個矩形的大小不大於\(s\)

  問你最少要用幾個矩形。

  \(n\leq 100,1\leq y\leq s\)

題解

  先把坐標離散化。

  猜(zheng)一個結論:最優解中任意兩個矩形的橫坐標只可能是相離或包含,不可能是相交。證明略。

  考慮區間DP。

  設\(f_{l,r,h}\)為覆蓋橫坐標\(l\sim r\),縱坐標\(>h\)的所有矩形需要的最少次數。

  枚舉\(l,r,h\),有兩種轉移:

  • 找到一個橫坐標\(i\),使得沒有任意一個矩形穿過\(i\)。枚舉\(i\)
    分治即可。
  • 放一個橫坐標為\(l\sim r\)的矩形,把高度設為上限。

  對於每一個\(h\),這一層的轉移是\(O(n^3)\)的,到下一層的轉移是\(O(n^2\log n)\)的,所以總時間復雜度就是\(O(n^4)\)

  用記憶化搜索可以跑得飛快。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<utility>
using namespace std;
typedef pair<int,int> pii;
int n,s;
pii a[110
]; int f[110][110][110]; int xx[110]; int yy[110]; int m1,m2; int d[110]; int gao(int x) { return x?s/x:0x3fffffff; } int gao(int l,int r,int h) { int &s=f[h][l][r]; if(~s) return s; while(l<=r&&d[l]<=h) l++; while(l<=r&&d[r]<=h) r--; if
(l>r) return s=0; int i; s=0x7fffffff; for(i=l;i<r;i++) s=min(s,gao(l,i,h)+gao(i+1,r,h)); int hh=gao(xx[r]-xx[l]); if(hh<=yy[h]) return s; int v=upper_bound(yy+1,yy+m2+1,hh)-yy-1; s=min(s,gao(l,r,v)+1); return s; } void solve() { scanf("%d%d",&n,&s); int i; for(i=1;i<=n;i++) { scanf("%d%d",&a[i].first,&a[i].second); xx[i]=a[i].first; yy[i]=a[i].second; } sort(xx+1,xx+n+1); sort(yy+1,yy+n+1); m1=unique(xx+1,xx+n+1)-xx-1; m2=unique(yy+1,yy+n+1)-yy-1; memset(f,-1,sizeof f); for(i=1;i<=m1;i++) d[i]=0; for(i=1;i<=n;i++) { a[i].first=lower_bound(xx+1,xx+m1+1,a[i].first)-xx; a[i].second=lower_bound(yy+1,yy+m2+1,a[i].second)-yy; d[a[i].first]=max(d[a[i].first],a[i].second); } int ans=gao(1,m1,0); printf("%d\n",ans); } int main() { #ifndef ONLINE_JUDGE freopen("b.in","r",stdin); freopen("b.out","w",stdout); #endif int t; scanf("%d",&t); while(t--) solve(); return 0; }

【XSY2693】景中人 區間DP