跳房子
题目
【题目描述】
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画n个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:
玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d 。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 gg ,但是需要注意的是,每 次弹跳的距离至少为1 。具体而言,当 g<d 时,他的机器人每次可以选择向右弹跳的距离为 d-g,d-g+1,d-g+2,d−g,d−g+1,d−g+2 ,…, d+g-2d+g−2 , d+g-1d+g−1 , d+gd+g ;否则(当g≥d 时),他的机器人每次可以选择向右弹跳的距离为 1 , 2 , 3 ,…, d+g-2d+g−2 , d+g-1d+g−1 , d+gd+g 。
现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。
【输入格式】
第一行三个正整数 n,d,k,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。
接下来 n行,每行两个正整数xi,si,分别表示起点到第i个格子的距离以及第i个格子的分数。两个数之间用一个空格隔开。保证 xi 按递增顺序输入。
【输出格式】
共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k分,输出-1。
【输入样例】
7 4 10 2 6 5 -3 10 3 11 -3 13 1 17 6 20 2
【输出样例】
2
【样例解释】
2个金币改进后, 小 R 的机器人依次选择的向右弹跳的距离分别为2,3,5,3,4,3, 先后到达的位置分别为2,5,10,13,17,20, 对应1,2,3,5,6,7 这6 个格子。这些格子中的数字之和15 即为小 R 获得的分数。
【数据规模】
本题共 10 组测试数据,每组数据 10 分。
对于全部的数据满足1≤n≤500000,1≤d≤2000,1≤xi,k≤109,∣si∣<105。
对于第1,2组测试数据, n≤10;
对于第3,4,5 组测试数据, n≤500
对于第6,7,8 组测试数据, d=1
解析
这题本蒟蒻用了些玄学优化,不用二分,不用单调队列,O(n2)的dp居然过了。
好,说说思路:
首先既然是dp,那么肯定少不了状态:
令f[i]表示到达i格子的最大分数,初值为极小值,
状态转移方程:f[j]=f[i]+s[j],具体的i与j后面会说。
我们在读入的时候,先累加所有分数大于0的,如果maxn(maxn表示累加分数后的结果)小于k,那么直接输出-1,maxn记得开long long不然后5个点全部都会输出-1(说多了都是泪)!
反之我们从0开始累加g的值,即while循环。
每次我们从0到n-1枚举i,如果bj[i]为真(bj[i]表示能否到达i这个格子,初始全为false,由于g是累加的,所以如果之前的g可以到达i,那么之后的g必然也可以到达i,所以只需要初始化bj一次就可以了),
便从i+1到n枚举j,如果x[j]-x[i]<=d+g(x如题目所述),即从i到j的距离小于等于能够跳的最远距离,就判断如果x[j]-x[i]>=max(1,d-g),即从i到j的距离大于等于能过跳的最短距离,
就令bj[j]=true,f[j]=max(f[j],f[i]+s[j]),如果f[j]>=k,那么直接输出当前的g;
如果x[j]-x[i]>d+g,即从i到j的距离大于能够跳的最远距离,那么直接退出j循环,因为x是递增的,那么如果当前点跳不到,后面的点肯定也是跳不到的。
Code
#include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> using namespace std; int read() { int num=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { num=(num<<1)+(num<<3)+ch-'0'; ch=getchar(); } return num*w; } int n,d,k,g,x[500100],s[500100],f[500100]; long long maxn; bool bj[500100]; int main() { memset(bj,false,sizeof(bj)); memset(f,0xcfcfcfcf,sizeof(f)); bj[0]=true,f[0]=0; n=read(),d=read(),k=read(); for(int i=1;i<=n;i++) { x[i]=read(),s[i]=read(); if(s[i]>0) maxn+=s[i]; } if(maxn<k) { cout<<"-1"; return 0; } while(1) { for(int i=0;i<n;i++) if(bj[i]) for(int j=i+1;j<=n;j++) { if(x[j]-x[i]<=d+g) { if(x[j]-x[i]>=max(1,d-g)) { bj[j]=true; f[j]=max(f[j],f[i]+s[j]); if(f[j]>=k) { cout<<g; return 0; } } } else break; } g++; } return 0; }