【动态规划】 Treasure Hunting

Treasure Hunting

在这里插入图片描述


分析:

题目中一个很关键的条件就是不能向下走
也就确定了我们这一题要一行一行去转移状态。
这意思是,当我们走到第i行的时候,第i行之前的宝藏已经全部被拿掉了。
当我们走第i行的时候,我们也需要把第i行的宝藏全部拿掉。
这个时候我们发现,由于我们处在第i行的时候,我们需要把这一行的宝藏全部拿掉,所以我们只需要关心第i行最靠左/右的宝藏位置即可
这也就确定了我们的dp状态:
f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示我们当前处在第i行左边/右边的最小步数是多少。
当前的状态可以从前一行的0/1转移过来
还有一点就是传送阵的位置,对当前位置,需要枚举里当前位置最近的左/右传送阵的位置,一次从前面的位置转移过来。
转移过来的时候需要往上走,所以还需要步数+1

还有一点需要注意的就是如果当前位置没有宝藏,那么当前位置就继承上一个位置的宝藏,同时步数+1,意思就是上一层的宝藏往上平移。

细节有点多,代码量有点大。
写的时候有点头晕(


#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5+100;
int n,m,k,q;
int Min[N],Max[N];
vector < int > a[N];
int l[N];
int f[N][3];

#define pb push_back

int Find(int x){
	int ll = 1 , r = q;
	while (ll+1<r){
		int Mid = ll+r>>1;
		if (l[Mid] >= x) r = Mid; else ll = Mid;
	}
	if (l[r] < x) return r; else return ll;
}

signed main(){
	cin>>n>>m>>k>>q;
	for (int i = 1; i <= n; i++) Min[i] = m+1;
	for (int i = 1; i <= k; i++){
		int x,y;
		cin>>x>>y;
		Min[x] = min(Min[x],y); Max[x] = max(Max[x],y);
	}
	for (int i = 1; i <= n; i++) if (Max[i]!=0) a[i].pb(Min[i]),a[i].pb(Max[i]);
	for (int i = 1; i <= q; i++) cin>>l[i];
	sort(l+1,l+q+1);
    int Maxh = 0;
    a[0].pb(1); a[0].pb(1);
    for (int i = 1; i <= n; i++) if (a[i].size()) Maxh = i;
    for (int i = 1; i <= n; i++) if (a[i].size() == 1) a[i].pb(a[i][0]);
	for (int i = 1; i <= n; i++)
	  if (a[i].size()) sort(a[i].begin(),a[i].end());
	if (a[1].size() == 0) a[1] = a[0];
	if (a[1].size() >= 2){
		f[1][0] = a[1][a[1].size()-1]-1+a[1][a[1].size()-1]-a[1][0]; f[1][1] = a[1][a[1].size()-1]-1;
	}
	for (int i = 2; i <= Maxh; i++){
		if (a[i].size() == 0){
			a[i] = a[i-1];
			f[i][0] = f[i-1][0]+1; f[i][1] = f[i-1][1]+1;
			continue;
		}
		int now1 = a[i][0] , now2 = a[i][a[i].size()-1]; int la1 = a[i-1][0] , la2 = a[i-1][a[i-1].size()-1];
		int po = Find(now2); int Po = l[po]; int s1 = f[i-1][0]+abs(la1-Po)+abs(Po-now2)+now2-a[i][0];
		if (po < q) Po = l[po+1]; int s2 = f[i-1][0]+abs(la1-Po)+abs(Po-now2)+now2-a[i][0];
		f[i][0] = min(s1,s2);
		Po = l[po];
		s1 = f[i-1][1]+abs(la2-Po)+abs(Po-now2)+now2-a[i][0];
		if (po < q) Po = l[po+1];
		s2 = f[i-1][1]+abs(la2-Po)+abs(Po-now2)+now2-a[i][0];
		s1 = min(s1,s2); f[i][0] = min(f[i][0],s1);
		
		po = Find(now1);  Po = l[po];
		s1 = f[i-1][0]+abs(la1-Po)+abs(Po-a[i][0])+now2-a[i][0];
		if (po < q) Po = l[po+1];
		s2 = f[i-1][0]+abs(la1-Po)+abs(Po-a[i][0])+now2-a[i][0];
		f[i][1] = min(s1,s2);
		Po = l[po];
		s1 = f[i-1][1]+abs(la2-Po)+abs(Po-a[i][0])+now2-a[i][0];
		if (po < q) Po = l[po+1];
		s2 = f[i-1][1]+abs(la2-Po)+abs(Po-a[i][0])+now2-a[i][0];
		s1 = min(s1,s2); f[i][1] = min(f[i][1],s1);
		f[i][0]++; f[i][1]++;
	}
	cout<<min(f[Maxh][0],f[Maxh][1]);
	return 0;
}
/*
5 5 10 3
3 1
3 2
3 5
1 5
2 1
2 5
1 2
1 4
3 3
2 4
2 1 5
*/
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值