Loading

5190. 【NOI2017模拟7.1】景中人

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;
}
posted @ 2022-01-20 20:38  Thunder_S  阅读(93)  评论(0编辑  收藏  举报