1. 程式人生 > >【STSRM10】dp只會看規律

【STSRM10】dp只會看規律

uniq pen 註意 矩形 open 全部 sizeof sed type

【算法】區間DP

【題意】平面上有n個點(xi,yi),用最少個數的底邊在x軸上且面積為S的矩形覆蓋這些點(在邊界上也算覆蓋),n<=100。

【題解】隨機大數據下,貪心幾乎沒有錯誤,貪心出奇跡啊!

f[i][j][h]表示區間i~j高度>=h的點全部被覆蓋的最少矩形。

首先離散化橫縱坐標,然後初始化每個f[i][i],然後進行區間DP(順次枚舉區間長度,左端點,高度從大到小)轉移如下。

f[i][j][h]=min(f[i][j][h],f[i][x][h]+f[x+1][j][h]),x=i~j-1

h2=s/(x[j]-x[i])(註意離散化)

f[i][j][h]=min(f[i][j][h],f[i][j][h2+1]+1)

為什麽這樣轉移是正確的?

考慮一個區間內情況,有以下兩種選擇:

1.分成兩個區間各自擺矩形並列。

2.在整個區間設置打矩形,則h2部分另外處理。

其它情況?直接在區間擺大矩形覆蓋全部等價於第二種情況,大區間h之上只有小區間的點等價於第一種情況,所以一共只有兩種情況。

技術分享
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int
read() { char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c==-)t=-1; do{s=s*10+c-0;}while(isdigit(c=getchar())); return s*t; } /*------------------------------------------------------------*/ const int inf=0x3f3f3f3f,maxn=110; struct cyc{int x,y;}a[maxn],b[maxn],c[maxn]; int n,f[maxn][maxn][maxn],ynum[maxn],tot,s;
bool cmp(cyc a,cyc b) {return a.x<b.x||(a.x==b.x&&a.y>b.y);} int main() { scanf("%d%d",&n,&s); for(int i=1;i<=n;i++){ a[i].x=read(); a[i].y=read(); } sort(a+1,a+n+1,cmp); int totx=1; c[totx]=a[1]; for(int i=2;i<=n;i++)if(a[i].x!=a[i-1].x)c[++totx]=a[i]; tot=n=totx; for(int i=1;i<=n;i++)a[i]=c[i]; for(int i=1;i<=n;i++)ynum[i]=a[i].y; sort(ynum+1,ynum+tot+1); tot=unique(ynum+1,ynum+tot+1)-ynum-1; for(int i=1;i<=n;i++){b[i].x=i;b[i].y=lower_bound(ynum+1,ynum+tot+1,a[i].y)-ynum;} ynum[++tot]=inf; memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++){for(int k=tot;k>b[i].y;k--)f[i][i][k]=0;for(int k=b[i].y;k>=0;k--)f[i][i][k]=1;} for(int p=2;p<=n;p++){ for(int i=1;i+p-1<=n;i++){ int j=i+p-1; for(int h=tot;h>=0;h--){ for(int x=i;x<j;x++)f[i][j][h]=min(f[i][j][h],f[i][x][h]+f[x+1][j][h]); int h2=lower_bound(ynum+1,ynum+tot+1,s/(a[j].x-a[i].x))-ynum; if(ynum[h2]==s/(a[j].x-a[i].x))h2++; f[i][j][h]=min(f[i][j][h],f[i][j][h2]+1); } } } printf("%d",f[1][n][0]); return 0; }
View Code

【STSRM10】dp只會看規律