【USACO2006 Mar】滑雪缆车 skilift

【USACO2006 Mar】 滑雪缆车 skilift

Time Limit 1000 ms
Memory Limit 131072 KBytes

Description

科罗拉多州的罗恩打算为奶牛建造一个滑雪场,为此要在山上规划一条缆车线路。

整座山可以用一条折线来描述,该折线有N个拐点,起点是1,终点是N。每个拐点的高度为Hi,相邻两个拐点之间的水平距离都是1。

缆车线路必须从起点开始修建,结束于终点。中间可以选择一些拐点安放缆绳的支柱,安全标准有两点:第一,缆绳的跨度有限制——相邻支柱的水平距离不能超过K;第二,缆绳的高度有限制——两根支柱之间的钢丝视作笔直的,在这座山的任何位置,钢丝都不能低于山的高度,但允许缆绳紧贴在山坡上或恰好穿过某个山峰。支柱相对于拐点的高度不计。

为了节约,罗恩希望修建的支柱越少越好,请帮他规划一下吧!当然,起点和终点上是一定要修建支柱的。

Input

第一行:两个用空格分开的整数:N和K,2 ≤ N ≤ 5000,1 ≤ K ≤ N − 1

第二行到N + 1行:第i + 1行有一个整数Hi,0 ≤ Hi ≤ ${10^9}$

Output

第一行:单个整数,表示最少需要修建的支柱数量

Sample Input

13 4
0
1
0
2
4
6
8
6
8
8
9
11
12

Sample Output

5

Hint

样例解释:支柱设在 1、5、7、9、13 号点处是最优方案。如果只设在 1、5、9、13 上,那么5 到9 就会有钢丝低于山坡的高度。如果只在 1、7、13 上修建,虽然高度符合要求,但这两段支柱的水平距离都超过了K。

 

Solution

身为菜鸡,打算再写一道dp

 

这道题的状态还是较容易设计的,即f[i]表示到第i个拐点所需设置的最少支柱数

 

那么一看数据范围,2000!!!!!!!!!!!

 

这就可以放心地乱搞写转移方程了

 

只要在前面的拐点中找到水平距离小于等于k且在这两个拐点之间所有拐点都不会插♂断这条缆绳的拐点,在这些拐点中求min(f[j])再加上改点即可

 

然后挂方程:$f[i] = \min \left\{ {\left. {f[j]} \right\}} \right. + 1,{\rm{1}} < = j < = i - 1$且j满足以上条件

 

那么我们现在就只剩下最后一个问题,如何判断缆绳不会被插♂断呢?

 

我们可以先做一个预处理,以(0,0)为坐标原点,把每个拐点写成一个点,显然,如果两个点之间没有点可以把缆绳插♂断,那么这两个点的所在的直线的斜率一定比前面那个点到两点之间每个点的斜率都要大

 

我们只需要用一个bool数组$b\left[ i \right]\left[ j \right]$来记录下第i个拐点是否能够达到第j个拐点

 

这样就能完美解决这道题了OwO

 

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std; 
bool b[5005][5005];
int f[5005],a[5005];
double x=1e-6;
bool check(double a,double b){return abs(a-b)<=x;}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=n;i++){
        double lim=-2147483640.0;
        for (int j=i+1;j<=n;j++) {
        if (check(lim,1.0*(a[j]-a[i])/(j-i))||lim<=1.0*(a[j]-a[i])/(j-i))    b[i][j]=true;
        lim=max(lim,1.0*(a[j]-a[i])/(j-i));
        } 
    }
    f[1]=1;
    for (int i=2;i<=n;i++){
        f[i]=2147483640;
        for (int j=1;j<=i-1;j++) 
        if (b[j][i]&&i-j<=k) f[i]=min(f[i],f[j]+1);
    }
    printf("%d",f[n]);
    return 0;
}

 

转载于:https://www.cnblogs.com/Cool-Angel/p/7728287.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值