Codeforces Round #577D. Treasure Hunting(二分+DP)

Treasure Hunting

You are on the island which can be represented as a n×mn×m table. The rows are numbered from 1 to n and the columns are numbered from 1 to m. There are kk treasures on the island, the ii-th of them is located at the position (ri,ci).

Initially you stand at the lower left corner of the island, at the position (1,1). If at any moment you are at the cell with a treasure, you can pick it up without any extra time. In one move you can move up (from (r,c) to(r+1,c)), left (from (r,c) to (r,c−1)), or right (from position(r,c) to(r,c+1)). Because of the traps, you can't move down.

However, moving up is also risky. You can move up only if you are in a safe column. There are qq safe columns: b1,b2,…,bq. You want to collect all the treasures as fast as possible. Count the minimum number of moves required to collect all the treasures.

Input

The first line contains integers n, mm, k and q (2≤n,m,k,q≤2⋅105, q≤m) — the number of rows, the number of columns, the number of treasures in the island and the number of safe columns.

Each of the next kk lines contains two integers ri,ci(1≤ri≤n, 1≤ci≤m) — the coordinates of the cell with a treasure. All treasures are located in distinct cells.

The last line contains qq distinct integers b1,b2,…,bq(1≤bi≤m) — the indices of safe columns.

Output

Print the minimum number of moves required to collect all the treasures.

Examples

input

3 3 3 2
1 1
2 1
3 1
2 3

output

6

input

3 5 3 2
1 2
2 3
3 1
1 5

output

Copy

8

input

3 6 3 2
1 6
2 2
3 4
1 6

output

15

Note

In the first example you should use the second column to go up, collecting in each row treasures from the first column.

In the second example, it is optimal to use the first column to go up.

In the third example, it is optimal to collect the treasure at cell (1;6), go up to row 2 at column 6, then collect the treasure at cell (2;2), go up to the top row at column 1 and collect the last treasure at cell (3;4). That's a total of 15 moves.

链接:https://codeforces.com/problemset/problem/1201/D

题意:k个点宝藏,q个安全列,最少步数收集所有宝藏。

每个位置可以往左、右走,只有在安全列位置时,才可以往上走,不可以向下走。

题解:只考虑每一行宝藏的最左边a[i][0]、最右边a[i][1],

因为将每一行宝藏收集完最短路径的终点只会发生在最左边、最右边。

最左边行结束dp[i][0],最右边结束dp[i][1],

并且i+1行继承第i行的计算时,

选择a[i][0](l)左右两边最近安全列q、w 和 a[i][1](r)左右两边最近安全列q、w,向上转移

寻找l或者r时的最近距离的q、w用二分查找,

状态转移方程每次选择最小值。

(画一下图就明白了~)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
int n,m,len;
long long a[200005][2];
long long b[200005];
long long er_b[200005][2];
long long dp[200005][2];
void solve()
{
	memset(dp,INF,sizeof(dp));
	for(int i=1;i<=m;i++)
	{
		er_b[i][0]=er_b[i][1]=INF;
		int top=lower_bound(b+1,b+len+1,i)-b;
		if(i==b[top])
		er_b[i][0]=er_b[i][1]=i;
		else if(top>1&&top<=len)
		{
			er_b[i][0]=b[top-1];
			er_b[i][1]=b[top];
		}
		else if(top==1)
		{
			er_b[i][1]=b[top];
		}
		else if(top==len+1)
		{
			er_b[i][0]=b[len];
		}
	}
	long long  l,r,top;
	if(a[1][0]!=INF)
	{
		dp[1][0]=abs(1-a[1][1])+abs(a[1][1]-a[1][0]);
		dp[1][1]=abs(1-a[1][1]);
		l=a[1][0];
		r=a[1][1];
		top=1;
	}
	else
	{
		dp[1][0]=0;
		dp[1][1]=0;
		top=1;
		l=r=1;
	}
	for(int i=2;i<=n;i++)
	{
		if(a[i][0]!=INF)
		{
			dp[i][0]=min(dp[i][0],(long long)(abs(er_b[l][0]-a[i][1])+abs(a[i][1]-a[i][0])+abs(er_b[l][0]-l)+dp[top][0]+(i-top)));	/*左->左*/
			dp[i][0]=min(dp[i][0],(long long)(abs(er_b[l][1]-a[i][1])+abs(a[i][1]-a[i][0])+abs(er_b[l][1]-l)+dp[top][0]+(i-top)));
			dp[i][0]=min(dp[i][0],(long long)(abs(er_b[r][0]-a[i][1])+abs(a[i][1]-a[i][0])+abs(er_b[r][0]-r)+dp[top][1]+(i-top)));	/*右->左*/
			dp[i][0]=min(dp[i][0],(long long)(abs(er_b[r][1]-a[i][1])+abs(a[i][1]-a[i][0])+abs(er_b[r][1]-r)+dp[top][1]+(i-top)));
							
			dp[i][1]=min(dp[i][1],(long long)(abs(er_b[l][0]-a[i][0])+abs(a[i][1]-a[i][0])+abs(er_b[l][0]-l)+dp[top][0]+(i-top)));
			dp[i][1]=min(dp[i][1],(long long)(abs(er_b[l][1]-a[i][0])+abs(a[i][1]-a[i][0])+abs(er_b[l][1]-l)+dp[top][0]+(i-top)));
			dp[i][1]=min(dp[i][1],(long long)(abs(er_b[r][0]-a[i][0])+abs(a[i][1]-a[i][0])+abs(er_b[r][0]-r)+dp[top][1]+(i-top)));
			dp[i][1]=min(dp[i][1],(long long)(abs(er_b[r][1]-a[i][0])+abs(a[i][1]-a[i][0])+abs(er_b[r][1]-r)+dp[top][1]+(i-top)));
			l=a[i][0];
			r=a[i][1];
			top=i;
		}
	}
	dp[n][0]=dp[top][0];
	dp[n][1]=dp[top][1];
	cout<<min(dp[n][0],dp[n][1])<<endl;
}
int main()
{
	int q;
	long long x,y;
	scanf("%d%d%d%d",&n,&m,&q,&len);
	for(int i=1;i<=200002;i++)
	a[i][0]=INF;
	for(int i=1;i<=q;i++)
	{
		scanf("%I64d%I64d",&x,&y);
		a[x][0]=min(a[x][0],y);
		a[x][1]=max(a[x][1],y);
	}
	for(int i=1;i<=len;i++)
	scanf("%I64d",&b[i]);
	sort(b+1,b+len+1);
	solve();
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值