中国石油大学Contest1391 - 2018年第三阶段个人训练赛第六场C: 守卫

大佬轻松讲完,我还是一脸懵,比赛只会做水题可怎么办呀,补题也补不动,思维太僵了,需要训练

题目描述

九条可怜是一个热爱运动的女孩子。 这一天她去爬山,她的父亲为了她的安全,雇了一些保镖,让他们固定地呆在在山的某些位置,来实时监视九条可怜,从而保护她。 具体来说,一座山可以描述为一条折线,折线的下方是岩石。这条折线有n个折点,每个折点上有一个亭子,第i个折点的坐标是(i,hi)。九条可怜只可能会在亭子处玩耍,那些保镖也只会在亭子处监视可怜。 由于技术方面的原因,一个保镖只能监视所有他能看得到的,横坐标不超过他所在位置的亭子。我们称一个保镖能看到一个亭子p,当且仅当他所在的亭子q和p的连线不经过任何一块岩石。特别地,如果这条连线恰好经过了除了p,q以外的亭子,那么我们认为保镖看不到可 怜。 雇佣保镖是一件很费钱的事情,可怜的父亲希望保镖越少越好。 可怜的父亲还希望得到详尽的雇佣保镖的方案,他知道有些亭子可能正在维修,他想对所有的1≤L≤R≤n计算:如果事先已知了只有区间[L,R]的亭子可以用来玩耍(和监视),那么最少需要多少个保镖,才能让[L,R]中的每一个亭子都被监视到。 可怜的父亲已经得到了一个结果,他希望和你核实他的结果是否正确。

 

输入

第一行输入一个整数n表示亭子的数目。
接下来一行n个整数,第i个整数hi表示第i个亭子的坐标是(i,hi)。

 

输出

对所有的L≤L≤R≤n计算:如果事先已知了可怜只会在[L,R]这个区间的亭子里面玩耍,那么最少需要多少个保镖,才能让[L,R]中的每一个亭子都被监视到。由于输出量太大,可怜的父亲只要你输出所有[L,R]的答案的异或即可。

 

样例输入

3
2 3 1

 

样例输出

3

 

提示

如果R−L+1≤2,那么答案显然是1。
如果L=1,R=n,那么答案是2,需要安排两个保镖在(2,3),(3,1)两个位置监视可怜。
对于30%的数据,n≤20。
对于70%的数据,n≤500。
对于100%的数据,n≤5000。
对于100%的数据,1≤hi≤109。

——————————————————————————假装分割线—————————————————————————

我也分析不出来,看了代码才倒推出思路的,有自己的思路了再来填坑吧,如果有错误,还请指正!

下面为我的代码来源传送门:

qq_41510496的博客洛谷P4563 [JXOI2018]守卫

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5000 + 5;
int h[maxn];
int dp[maxn][maxn];
int n, k;

bool Cansee(int l, int mh, int r)
{//如果在r点经过当前最高点mh点可以看见l点返回真,方法多种,比较斜率即可
    //return (h[r] - h[mh])/(r - mh) > (h[mh] - h[l])/(mh - l);
    return (h[r] - h[mh])/(r - mh) > (h[r] - h[l])/(r - l);
}

int main()
{
    scanf("%d", &n);
    for (int i = 1;i <= n;++i)
        scanf("%d", &h[i]);
    int ans = 0;
    for (int i = 1; i <= n; ++i){
        dp[i][i] = 1;//只需要一个
        int sum = 1, mh = i;//sum为mh到i点最小需要的数量, mh为下面循环时i能看到最远的点
        ans ^= dp[i][i];//每一段区间都需要异或,i-i也不例外
        for (int j = i - 1; j; --j){//区间枚举
            if (mh == i||Cansee(j, mh, i)){//如果可以看见j点
                sum += min(dp[j + 1][mh - 1], dp[j + 1][mh]);//注意当dp[i][j]中i>j时答案为0
                mh = j;//更新mh点,保证mh为i能看到最远的点
            }
            //此时sum中mh点没有守卫,由于mh点不需要守卫,需要加上j-mh点或j-(mh - 1)范围中需要守卫最少的数量
            dp[j][i] = sum + min(dp[j][mh - 1], dp[j][mh]);
            ans ^= dp[j][i];//答案需要异或
        }
    }
    printf("%d\n", ans);
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值