您的位置:首页 > 其它

POJ 1661 Help Jimmy (DP)

2017-04-19 20:39 459 查看
"Help Jimmy" 是在下图所示的场景上完成的游戏。



场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。

Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。

设计一个程序,计算Jimmy到底地面时可能的最早时间。
Input
第一行是测试数据的组数t(0 <= t <= 20)。每组测试数据的第一行是四个整数N,X,Y,MAX,用空格分隔。N是平台的数目(不包括地面),X和Y是Jimmy开始下落的位置的横竖坐标,MAX是一次下落的最大高度。接下来的N行每行描述一个平台,包括三个整数,X1[i],X2[i]和H[i]。H[i]表示平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1 <= N <= 1000,-20000 <= X, X1[i], X2[i] <=
20000,0 < H[i] < Y <= 20000(i = 1..N)。所有坐标的单位都是米。

Jimmy的大小和平台的厚度均忽略不计。如果Jimmy恰好落在某个平台的边缘,被视为落在平台上。所有的平台均不重叠或相连。测试数据保证问题一定有解。

Output
对输入的每组测试数据,输出一个整数,Jimmy到底地面时可能的最早时间。

题意是中文简单易懂。
对于每一块板子,到达板子时,要么向左,要么向右,那么就正向推可以设一个dp,dp[i][0]/dp[i][1]。
dp[i][0],表示到达第 i 块板子左端点的最短时间。

dp[i][1],表示到达第 i 块板子右端点的最短时间。
先对高度排序,让板子从高到低排序。
对于当前 i 板子,只能由从 1 到 i-1 转移来,那么就暴力枚举前 i-1 块板子,然后再根据规则推就行了。
但是因为上面的板子挡住了现在的板子,那么就可能到达不了这块板子,所有这里要判断,是否能从第 j 块板子到第 i 块板子。
但是怎么来判断呢:
可以将上面的板子,两个坐标看成区间,那么就把区间更新为 1 就可以了。
如果 j 块板子的端点是 1,就表示不能从这块板子到达 i 板子。
这里就要引入线段树的区间更新,模板就不说了。
但是区间的 范围是 -20000-20000
但是最多只有2000+4个点(起点+终点),那么离散化一下就可以了。
最后这段毒眼睛的代码就出来了。

其实有更简单的方法,dp[i][0]/dp[i][1],表示第 i 块板子从左/右端点到达地面的最短时间,这个也很好推的。

感觉自己弱爆了。

#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#include<math.h>
#include<map>
#include<iostream>
using namespace std;
#define maxn 1005
#define ll long long
struct nnode
{
int x, y, h;
} a[maxn];
int n, dp[maxn][2], use[5000];
vector<int> v;
struct node
{
int id,l,r,maxx,add;
} tree[30000 * 4];
int getid(int x){   return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;}
void pushdown(int id)
{
if(tree[id].add != -1)
{
tree[id * 2].add = tree[id].add;
tree[id * 2 + 1].add = tree[id].add;
tree[id * 2].maxx = tree[id].add;
tree[id * 2 + 1].maxx = tree[id].add;
tree[id].add = -1;
}
}
void update(int id, int ql, int qr, int newval)
{
int l = tree[id].l;
int r = tree[id].r;
int mid = (l + r) / 2;
if(r < ql || qr < l)
return;
if(ql <= l && qr >= r)
{
tree[id].add = newval;
tree[id].maxx = newval;
return ;
}
pushdown(id);
if(mid >= qr)
update(id * 2, ql, qr, newval);
else if((mid + 1) <= ql)
update(id * 2 + 1, ql, qr, newval);
else
{
update(id * 2, ql, qr, newval);
update(id * 2 + 1, ql, qr, newval);
}
tree[id].maxx = max(tree[id * 2].maxx, tree[id * 2 + 1].maxx);
}
int querymax(int id, int ql, int qr)
{
int l = tree[id].l;
int r = tree[id].r;
int mid = (l + r) / 2;
if(r < ql || l > qr)
return 0;
if(ql <= l && qr >= r)
return tree[id].maxx;
pushdown(id);
if(mid >= qr)
return querymax(id * 2, ql, qr);
else if((mid + 1) <= ql)
return querymax(id * 2 + 1, ql, qr);
else
return max(querymax(id * 2, ql, qr), querymax(id * 2 + 1, ql, qr));
}
void build(int id, int l, int r)
{
tree[id].l = l;
tree[id].r = r;
tree[id].add = -1;
if(l == r)
{
tree[id].maxx = 0;
return ;
}
int mid = (l + r) / 2;
build(id * 2, l, mid);
build(id * 2 + 1, mid + 1, r);
tree[id].maxx = max(tree[id * 2].maxx, tree[id * 2 + 1].maxx);
}
bool cmp(nnode a, nnode b) {    return a.h > b.h;   }
int main()
{
int smax, t, dx, dy;
scanf("%d", &t);
while(t--)
{
memset(dp, 0x7f, sizeof(dp));
scanf("%d%d%d%d", &n, &a[0].x, &a[0].h, &smax);
a[0].y = a[0].x;                                   //加入起点
for(int i = 1; i <= n; i++)
scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].h);
n++; a
.x = -20000;a
.y = 20000;a
.h = 0;    //加入终点
for(int i=0;i<=n;i++)
{
v.push_back(a[i].x);
v.push_back(a[i].y);
}
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
for(int i = 0; i <= n; i++)
{
int dx = a[i].x, dy = a[i].y;
a[i].x = getid(a[i].x);
a[i].y = getid(a[i].y);
use[a[i].x] = dx;
use[a[i].y] = dy;
}
build(1, 1, 3000);
sort(a, a + n + 1, cmp);
dp[0][0] = dp[0][1] = 0;
for(int i = 1; i <= n; i++)
{
update(1, 1, 3000, 0);
for(int j = i - 1; j >= 0; j--)
{
if(a[j].h - a[i].h > smax || a[i].x > a[j].y || a[i].y < a[j].x)
continue;
int th = a[j].h - a[i].h;
if(a[i].x <= a[j].x && a[i].y < a[j].y) //0
{
int maxx = querymax(1, a[j].x, a[j].x);
if(maxx == 1)
{

4000
update(1, a[j].x, a[j].y, 1);
continue;
}
int l = use[a[j].x] - use[a[i].x];
int r = use[a[i].y] - use[a[j].x];
if(i==n) l=r=0;
dp[i][0] = min(dp[i][0], dp[j][0] + th + l);
dp[i][1] = min(dp[i][1], dp[j][0] + th + r);
update(1, a[j].x, a[j].y, 1);
}
else if(a[i].x > a[j].x && a[j].y <= a[i].y) //1
{
int maxx = querymax(1, a[j].y, a[j].y);
if(maxx == 1)
{
update(1, a[j].x, a[j].y, 1);
continue;
}
int l = use[a[j].y] - use[a[i].x];
int r = use[a[i].y] - use[a[j].y];
if(i==n) l=r=0;
dp[i][0] = min(dp[i][0], dp[j][1] + th + l);
dp[i][1] = min(dp[i][1], dp[j][1] + th + r);
update(1, a[j].x, a[j].y, 1);
}
else if(a[i].x <= a[j].x && a[i].y >= a[j].y) // 0 1
{
int maxx1 = querymax(1, a[j].x, a[j].x);
if(maxx1 == 0)
{
int l = use[a[j].x] - use[a[i].x];
int r = use[a[i].y] - use[a[j].x];
if(i==n) l=r=0;
dp[i][0] = min(dp[i][0], dp[j][0] + th + l);
dp[i][1] = min(dp[i][1], dp[j][0] + th + r);
}
int maxx2 = querymax(1, a[j].y, a[j].y);
if(maxx2 == 0)
{
int l = use[a[j].y] - use[a[i].x];
int r = use[a[i].y] - use[a[j].y];
if(i==n) l=r=0;
dp[i][0] = min(dp[i][0], dp[j][1] + th + l);
dp[i][1] = min(dp[i][1], dp[j][1] + th + r);
}
update(1, a[j].x, a[j].y, 1);
}
}
}
printf("%d\n", min(dp
[0], dp
[1]));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 线段树