codeforces 1201 D. Treasure Hunting(二分+贪心)

http://codeforces.com/contest/1201/problem/D

题意:
有一个n*m的方格,方格上有k个宝藏,一个人从(1,1)出发,可以向左或者向右走,但不能向下走。给出q个列,在这些列上可以向上走,其他列不能向上走。可以重复经过同一个点。求从(1,1)出发,经过所有宝藏的最短路径长度

思路:这道题目,思路比较好想,就是贪心的选择每层最左边最右边宝藏的相邻的四个楼梯,然后和前面求得情况求走的距离的累加,枚举每一个出口,每次选择距离最小的就是该出口的最小值。
但是这道题细节很多
左右最近楼梯通过二分求得后,枚举每一个出口,和之前情况取最小值
分别讨论最左宝藏和最右宝藏在出口左端右端情况
最后一层只需要讨论到左右宝藏的情况
最后加上最低高度

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define fi first
#define se second
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
 
typedef long long ll;
typedef pair<ll, ll> P;
typedef pair<P, ll> LP;
const ll inf = 1e17 + 10;
const int N = 3e6 + 10;
const ll mod = 10007;
const int base=131;
tr1::unordered_map<ll,ll> mp;
 
ll n,m,id,t,x,y,k,q;
ll num[N],vis[N],cnt;
ll l[N],r[N],a[N];
ll ans,flag;
//ll tree[N],tag[N];
set<ll> st;
vector<ll> v;
 
 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
 
	cin>>n>>m>>k>>q;
	ll top=1;
	l[1]=r[1]=1;
	for(int i=1;i<=k;i++)
	{
		cin>>x>>y;
		top=max(top,x);
		if(!l[x]) l[x]=y;
		else l[x]=min(l[x],y);
		r[x]=max(r[x],y);
	}
	for(int i=1;i<=q;i++) cin>>x,st.insert(x);
 
	vector<P> pre;
	pre.push_back(P(0,1));
	for(int i=1;i<top;i++)
	{
		if(l[i]==0&&r[i]==0) continue;
		cnt=0;
		auto pos1=st.lower_bound(l[i]);
 
		if(pos1!=st.end()) a[++cnt]=*pos1;
		if(pos1!=st.begin()) a[++cnt]=*(--pos1);
 
 
		auto pos2=st.lower_bound(r[i]);
		if(pos2!=st.end()) a[++cnt]=(*pos2);
		if(pos2!=st.begin()) a[++cnt]=*(--pos2);
 
		sort(a+1,a+cnt+1);
		cnt=unique(a+1,a+cnt+1)-(a+1);
	//	cout<<"louti: ";for(int i=1;i<=cnt;i++) cout<<a[i]<<" ";cout<<endl;
		vector<P> tmp;
		for(int j=1;j<=cnt;j++)
		{
			ll res=inf,out=a[j];//枚举每一个出口
			//show3("out",j,a[j])
			for(auto p:pre)
			{
				ll sum=p.fi,prex=p.se;
				if(prex<=out)
				{
					if(l[i]<=prex) sum+=2*abs(l[i]-prex);
					if(r[i]>=out) sum+=2*abs(r[i]-out);
					//if(out==1&&prex==1) cout<<"asfd"<<endl,show3(r[i],out,i)
				}
				else
				{
					if(r[i]>=prex) sum+=2*abs(r[i]-prex);
					if(l[i]<=out) sum+=2*abs(l[i]-out);
				}
				sum+=abs(out-prex);
				res=min(sum,res);
			}
			//show3(i,res,out)
			tmp.push_back(P(res,out));
		}
		pre=tmp;
		//for(auto c:pre) cout<<c.fi<<" "<<c.se<<"    ";cout<<endl;
	}
	ans=inf;
	int i=top;
	for(auto p:pre)
	{
		ll sum=p.fi,prex=p.se;
		if(prex<=l[i])
		{
			sum+=abs(r[i]-prex);
		}
		else
		{
			if(prex>=r[i]) sum+=abs(l[i]-prex);
			else
			{
				sum+=min(r[i]-prex,prex-l[i])+r[i]-l[i];
			}
		}
		ans=min(sum,ans);
	}
	cout<<ans+top-1<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值