Description
有 \(n\) 个看风景的人在桥上。桥可以看成一个二维平面,那么每个人的位置都可以用一个坐标来表示。
Yazid 突发奇想,想用矩形把他们都覆盖住。
Yazid 发现,只需要用 1 个巨大的矩形就可以做到这点。于是他规定单个矩形的面积不能超过 \(S\) 。
Yazid 还觉得桥的下边的栏杆很优秀,于是他又规定了矩形的一条边必须贴着下栏杆 (直线 \(y=0\) )。
这下可把辣鸡蒟蒻 Yazid 难住了,他找到了即将参加 NOI 的你,请你告诉他,他至少要用几个矩形才能覆盖所有的景中人呢?
Solution
区间 \(\mathrm{dp}\)。
设 \(f_{i,j}\) 表示包含 \([i,j]\) 的最小矩形数。
先将点排序并且离散化,注意到在 \(x\) 相同的情况下,\(y\) 小的相对于 \(y\) 大的来说是没有用的,因此可以不管他们。
转移过程:
我们先求出矩形的最高高度 \(up\),并且从 \(i\) 开始,从左往右找到第 1 个高度大于 \(up\) 的点,记为 \(l\)。也从 \(j\) 开始从右往左找到第 1 个高度大于 \(up\) 的点。
如果 \(l>r\) 表示 \([i,j]\) 可以由一个矩形覆盖,那么 \(f_{i,j}=1\)。否则 \(f_{i,j}=f_{l,r}+1\)。
我们再枚举一个 \(k(i\le k<j)\),那么 \(f_{i,j}=f_{i,k}+f_{k+1,j}\)。
另外呢,如果求出了高度大于 \(up\) 的点可以由多少个矩形覆盖,也可以进行转移。
先求出高度大于 \(up\) 的点,并且记录在 \(d\) 中,假设这样的点有 \(num\) 个。
设 \(g_{i}\) 表示当前为第 \(i\) 个高度大于 \(up\) 的点,枚举 \(j(j\le i)\),则 \(g_{i}=g_{j-1}+f_{d_j,d_i}\)。
那么 \(f_{i,j}=g_{num}+1\)。
最后答案为 \(f_{1,n}\)。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 105
#define inf 123456789
using namespace std;
struct node
{
int x,y;
}p[N];
int T,n,s,ans,num,f[N][N],d[N],g[N];
bool cmp(node x,node y)
{
if (x.x<y.x) return true;
if (x.x>y.x) return false;
return x.y>y.y;
}
int main()
{
freopen("scene.in","r",stdin);
freopen("scene.out","w",stdout);
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&s);
num=1;
for (int i=1;i<=n;++i)
scanf("%d%d",&p[i].x,&p[i].y);
sort(p+1,p+n+1,cmp);
for (int i=2;i<=n;++i)
if (p[i].x!=p[num].x) p[++num]=p[i];
n=num;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for (int len=1;len<=n;++len)
{
for (int i=1;i+len-1<=n;++i)
{
int j=i+len-1;
f[i][j]=inf;
int up=s/max(p[j].x-p[i].x,1);
int l=i,r=j;
while (l<=j&&p[l].y<=up) ++l;
while (r>=i&&p[r].y<=up) --r;
if (l>r)
{
f[i][j]=1;
continue;
}
f[i][j]=f[l][r]+1;
for (int k=i;k<j;++k)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
num=0;
for (int k=i;k<=j;++k)
if (p[k].y>up) d[++num]=k;
for (int i=1;i<=num;++i)
g[i]=inf;
g[0]=0;
for (int k=1;k<=num;++k)
for (int o=1;o<=k;++o)
g[k]=min(g[k],g[o-1]+f[d[o]][d[k]]);
f[i][j]=min(f[i][j],g[num]+1);
}
}
printf("%d\n",f[1][n]);
}
return 0;
}