题目的意思就是有一群牛它们懒得动,不会跳跃越过篱笆,现在它们要从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);
}