• 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;
    }
    
  • 相关阅读:
    java中的成员变量和局部变量
    多线程实现输出当前时间,和猜数字游戏
    JDBC
    jQuery和原生JS的对比
    JavaScript有趣的知识点
    MySQL的数据类型
    行级元素和块级元素
    重定向和请求转发的区别
    JSP九大内置对象
    Python练习
  • 原文地址:https://www.cnblogs.com/Livingston/p/15827976.html
Copyright © 2020-2023  润新知