poj 2374 求横向距离和最小 线段树 + 动态规划

55 篇文章 2 订阅
4 篇文章 0 订阅

 题目的意思就是有一群牛它们懒得动,不会跳跃越过篱笆,现在它们要从S点走到最下面的谷仓*,它们往下走碰到篱笆就选择往左或往右沿着篱笆走,走到篱笆端点的时候在往下走碰到篱笆再选择往左或往右沿着篱笆走,如此,知道到达最下面的谷仓。要你求出到达谷仓沿着篱笆走的距离和最短是多少?

代码copy了这位神牛的http://www.cppblog.com/varg-vikernes/archive/2012/01/29/109213.html

解题思路:

动态规划 + 线段树

牛往下走碰到篱笆往左或往右走的距离取决于之前走的挡板,从上面的挡板右端点下来,碰到了篱笆,再往右走,那么距离要加

上这段距离,要是没有碰到这个挡板就直接往下走了,不用加上这块挡板的相关距离,若是往左走,则要加上左端点与上面挡板

右端点的距离差。

所以要用dp[i].A表示到达挡板i往左走到端点的最小距离和,dp[i].B表示到达挡板i往右走的最小距离和。

dp[i].A = min(dp[j].A + F[i].A - F[j].A, dp[k].B + F[k].B - F[i].A)           F[i].A, F[i].B表示挡板左右坐标, j

, k 是i 挡板上面的某个挡板, 挡板的j的左端点A在挡板i的线段内,挡板k的右端点B在挡板i的线段内。同理dp[i].B也是同样

的方法计算。最后求出目标点跟左右两边的落地点的距离AL, BR,然后比较落地点AL + dp[N].A和 BR + dp[N].B的大小取最小的

由于输入的坐标有负数,所以我们所有坐标都加上一个正数使坐标转换成>0的数。规定了篱笆的一个最大范围。

然后要注意输入是从下往上输入篱笆的坐标,所以我们可以转成从下往上动态规划算出最小距离

 插入线段时标记线段对应的挡板序号,查询端点时,取包含端点的挡板号最大的线段来计算。

#include <stdio.h>
#include <math.h>

typedef struct str_fence
{
	int A_i;
	int B_i;
} fence;

#define MAX_N 50001
#define MAX_R 100010
#define max(a,b) (a) > (b) ? (a) : (b)
#define min(a,b) (a) > (b) ? (b) : (a)
void insert(int index,int start,int end,int left,int right,int val);
int query(int index,int start,int end,int pos);
int cal_min(int i,int pos);
fence F[MAX_N],dp[MAX_N];
int tree[MAX_N*16];
int N,S;
int main()
{
	int i;
	scanf("%d%d",&N,&S);
	S += MAX_R;
	for(i = 1; i <= N; i++)
	{
		scanf("%d%d",&F[i].A_i,&F[i].B_i);
		F[i].A_i += MAX_R;
		F[i].B_i += MAX_R;
		dp[i].A_i = cal_min(query(1,0,MAX_R*2,F[i].A_i),F[i].A_i);
		dp[i].B_i = cal_min(query(1,0,MAX_R*2,F[i].B_i),F[i].B_i);
		insert(1,0,MAX_R*2,F[i].A_i,F[i].B_i,i);
	}
	printf("%d\n",min(S-F[N].A_i+dp[N].A_i,F[N].B_i-S+dp[N].B_i));
	return 0;
}

void insert(int index,int start,int end,int left,int right,int val)
{
	if(start == left && end == right)
	{
		tree[index] = val;
		return;
	}
	int mid = (start + end)/2;
	if(right <= mid)
		insert(index*2,start,mid,left,right,val);
	else if(left > mid)
		insert(index*2+1,mid+1,end,left,right,val);
	else
	{
		insert(index*2,start,mid,left,mid,val);
		insert(index*2+1,mid+1,end,mid+1,right,val);
	}
}

int query(int index,int start,int end,int pos)
{
	if(start == pos && end == pos)
		return tree[index];
	int val;
	int mid = (start + end) / 2;
	if(pos <= mid)
		val = query(index*2,start,mid,pos);
	else if(pos > mid)
		val = query(index*2+1,mid+1,end,pos);
	return max(val,tree[index]);
}

int cal_min(int i,int pos)
{
	if(!i)
		return abs(pos - MAX_R);
	else
		return min(F[i].B_i-pos+dp[i].B_i,pos-F[i].A_i+dp[i].A_i);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值