大佬轻松讲完,我还是一脸懵,比赛只会做水题可怎么办呀,补题也补不动,思维太僵了,需要训练
题目描述
九条可怜是一个热爱运动的女孩子。 这一天她去爬山,她的父亲为了她的安全,雇了一些保镖,让他们固定地呆在在山的某些位置,来实时监视九条可怜,从而保护她。 具体来说,一座山可以描述为一条折线,折线的下方是岩石。这条折线有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;
}