hdu 2155 小黑的镇魂曲(dp)

感觉蛮坑的一道题。

题意很像一个叫“是男人下100层”的游戏。不过多了个时间限制,要求在限定时间内从某一点下落到地面。还多了个最大下落高度,一次最多下落这么高,要不然会摔死。

一开始想dp的,然后想了半天想不到状态,因为如果以下落点位状态的话,一个板子上会有许多状态,然后就没法继续下去了。

然后试着证明贪心,结果证明不出来。贪心也用不了了。

其实放弃了,在比赛结束后又去看了看,然后讨论,然后吧啦吧啦吧啦……还是做不出来。结果还是搜了题解,唉,有些挫败感。

输入:

首行输入整数t,表示共t组数据。

接下来每组数据首行输入n, x, h, maxn, m。分别表示板子数量,初始位置的横坐标,初始位置的高度,最大一次可以下落的高度,限定时间。

接下来n行,每行3个整数,l, r, h。表示第i个板子的左边的坐标,右边的左边,高度。

输出:

如果在m秒内(含m秒)能到达地面,输出“NO”,否则输出“YES”。

题解用的还是dp,只是他稍微拐了个弯,设的状态是每块板子的左端坐标和右端坐标,这样就解决了从不同高板子下落到同一低板子上时会产生不同状态的问题了,不得不感慨,dp真神奇。

核心——判断从第i块板子上的左,右端下落到第j块板子的左,右端(如果可以的话)是否优于原本第j块板子左右端的时间。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7
 8 const int N = 1010;
 9
10 struct Tp
11 {
12     int l, r, h;
13 }tp[N];
14
15 int dp[N][2];
16 int x, maxn, h;
17 int n, m, t;
18
19 bool cmp(Tp x, Tp y)
20 {
21     return x.h > y.h;
22 }
23
24 void init()
25 {
26     scanf("%d%d%d%d%d", &n, &x, &h, &maxn, &m);
27     tp[0].l = x; tp[0].r = x; tp[0].h = h;          //出发点也设为一个板子,左右端都是x,高度为h
28     for(int i = 1; i <= n; i++)
29     {
30         scanf("%d%d%d", &tp[i].l, &tp[i].r, &tp[i].h);
31     }
32     tp[n+1].l = -1; tp[n+1].r = N; tp[n+1].h = 0;   //地面
33     sort(tp, tp+n+1, cmp);          //排序,为了后面可以大规模的break,节省时间
34     dp[0][0] = dp[0][1] = 0;        //初始状态
35     for(int i = 1; i <= n+1; i++)
36     {
37         dp[i][0] = dp[i][1] = 1000010;
38     }
39 }
40
41 void Dp()
42 {
43     for(int i = 0; i <= n; i++)
44     {
45         bool p1 = 0, p2 = 0;
46         for(int j = i+1; j <= n+1; j++)         //从i上下落的j上
47         {
48             if(tp[i].h-tp[j].h > maxn) break;        //如果会摔死,则进入下一块i板子。
49
50             if(tp[i].l >= tp[j].l && tp[i].l <= tp[j].r && !p1) //如果能从i的左端下落到j上
51             {
52                 p1 = 1;             //i的左端只能下落的某一块固定板子上,无法下落到两个板子上
53                 if(j != n+1)        //如果不是落到地面,则需要计算纵向位移时间和横向位移时间
54                 {
55                     dp[j][0] = min(dp[j][0], dp[i][0]+tp[i].h-tp[j].h+tp[i].l-tp[j].l);
56                     dp[j][1] = min(dp[j][1], dp[i][0]+tp[i].h-tp[j].h+tp[j].r-tp[i].l);
57                 }
58                 else                //落到地面,则不需要计算横向位移时间
59                 {
60                     dp[j][0] = min(dp[j][0], dp[i][0]+tp[i].h-tp[j].h);
61                     dp[j][1] = min(dp[j][1], dp[i][0]+tp[i].h-tp[j].h);
62                 }
63             }
64             if(tp[i].r >= tp[j].l && tp[i].r <= tp[j].r && !p2) //如果能从i的右端下落到j上
65             {
66                 p2 = 1;         //i的右端同样只能下落的某一块固定板子上,无法下落到两个板子上
67                 if(j != n+1)
68                 {
69                     dp[j][0] = min(dp[j][0], dp[i][1]+tp[i].h-tp[j].h+tp[i].r-tp[j].l);
70                     dp[j][1] = min(dp[j][1], dp[i][1]+tp[i].h-tp[j].h+tp[j].r-tp[i].r);
71                 }
72                 else
73                 {
74                     dp[j][0] = min(dp[j][0], dp[i][1]+tp[i].h-tp[j].h);
75                     dp[j][1] = min(dp[j][1], dp[i][1]+tp[i].h-tp[j].h);
76                 }
77             }
78         }
79     }
80 }
81
82 void output()
83 {
84     if(dp[n+1][0] <= m || dp[n+1][1] <= m) printf("NO\n");
85     else printf("YES\n");
86 }
87
88 int main()
89 {
90     //freopen("test.txt", "r", stdin);
91     scanf("%d", &t);
92     while(t--)
93     {
94         init();
95         Dp();
96         output();
97     }
98     return 0;
99 }

时间: 2024-10-05 17:04:13

hdu 2155 小黑的镇魂曲(dp)的相关文章

小黑的镇魂曲(HDU2155:贪心+dfs+奇葩解法)

题目:点这里 题目的意思跟所谓的是英雄就下100层一个意思--在T秒内能够下到地面,就可以了(还有一个板与板之间不能超过H高). 接触这题目是在昨晚的训练赛,当时拍拍地打了个贪心+dfs,果断跟我想的一模一样,TLE了. 赛后我在宿舍里修改了好几次--均无果.后来,我大胆地假设,估计是最后两组出问题TLE的..于是我就在程序里,指定在最后两组输出yes或者no,就这样奇葩地AC了-- 我实验了三次,总共有2*2种可能--(差点就觉得人品差到不行了) 终于AC了.当然,平时练习真心不要这样子,但是

hdu 3555 Bomb(数位dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意:就是给你一个数n,判断从0到n有多少个数含有数字49...... 是不是觉得跟hdu2089很相似呀... 思路:跟hdu2089一样的,注意给出的数比较大,所以这儿用__int64  .... code: #include<cstdio> #include<iostream> #include<cstring> #include<algorithm&

HDU 1231 最大连续子序列 DP题解

典型的DP题目,增加一个额外要求,输出子序列的开始和结尾的数值. 增加一个记录方法,nothing special. 记录最终ans的时候,同时记录开始和结尾下标: 更新当前最大值sum的时候,更新开始节点. const int MAX_N = 10001; long long arr[MAX_N]; int N, sta, end; long long getMaxSubs() { long long sum = 0, ans = LLONG_MIN; int ts = 0; for (int

[ACM] hdu 2089 不要62(数位Dp)

不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 19043    Accepted Submission(s): 6442 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就

HDU 4901 The Romantic Hero(DP)

HDU 4901 The Romantic Hero 题目链接 题意:给定一个序列,要求找一个分界点,然后左边选一些数异或和,和右边选一些数且和相等,问有几种方法 思路:dp,从左往右和从右往左dp,求出异或和且的个数,然后找一个分界点,使得一边必须在分界点上,一边随意,然后根据乘法原理和加法原理计算 代码: #include <cstdio> #include <cstring> typedef __int64 ll; const int N = 1024; const int

HDU 1160 FatMouse&#39;s Speed DP题解

本题就先排序老鼠的重量,然后查找老鼠的速度的最长递增子序列,不过因为需要按原来的标号输出,故此需要使用struct把三个信息打包起来. 查找最长递增子序列使用动态规划法,基本的一维动态规划法了. 记录路径:只需要记录后继标号,就可以逐个输出了. #include <stdio.h> #include <algorithm> using namespace std; const int MAX_N = 1005; struct MouseSpeed { int id, w, s; b

HDU 2089 不要62(数位DP,三种姿势)

HDU 2089 不要62(数位DP,三种姿势) ACM 题目地址:HDU 2089 题意: 中文题意,不解释. 分析: 100w的数据,暴力打表能过 先初始化dp数组,表示前i位的三种情况,再进行推算 直接dfs,一遍搜一变记录,可能有不饥渴的全部算和饥渴的部分算情况,记录只能记录全部算(推荐看∑大的详细题解Orz) 代码: 1. 暴力 (以前写的) /* * Author: illuz <iilluzen[at]gmail.com> * File: 2089_bf.cpp * Create

HDU 2084 数塔 --- 入门DP

HDU 2084 数塔 从下往上递推,状态转移方程 dp[i][j] = max( dp[i+1][j], dp[i+1][j+1]) + a[i][j]; /* HDU 2084 数塔 --- 入门DP */ #include <cstdio> const int N = 105; int dp[N][N]; int MAX(int a, int b){ return a > b ? a : b; } int main() { #ifdef _LOCAL freopen("D

hdu 1160 FatMouse&#39;s Speed(dp)

http://acm.hdu.edu.cn/showproblem.php?pid=1160 #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <queue> using namespace std; struct Node { int w,s,id,fa; }; Node mice[1000+10]; int dp[100