CF1066F Yet another 2D Walking 思维,最短路,啥b建图

题目链接

https://www.luogu.com.cn/problem/CF1066F

题意

二维地图,起始(0,0),给出若干点,要求遍历每一点,两点距离为曼哈顿距离,点有分层level=MAX(X,Y),遍历下一层的点之前一定要遍历全上一层的点,求最短距离。

思路

CF分区是个2100分DP,本来不打算补了(丢给队友.JPG)但见到一篇最短路的题解,看完直呼神仙做法,索性补了一下。

首先需要发现一个特性,我们对于每一层的点,要想保证这一层走的最优,那么这一层遍历的起点和终点都应该是这一层点的"端点"(一层的点可以看作是一个180°旋转的L,我们把这个倒L横竖两条班的顶点称为端点)。因为如果是在不是端点的平凡点上开始/结束,那么在这一层遍历时,一定会出现在这个倒L上重复行走的情况。

那么我们发现了这些特殊点,然后呢?我们用最短路有以下几点需要处理

  1. 如何保证分层条件?即我们如何建图才能保证一层遍历完才能到下一层?

第一条比较容易解决,我们只需要将点分层,只在上下层间连边,将层级间便利花费隐含在边权之中,不跑坐标点之间的距离最短路,而是跑层级移动最短路即可。

但随之而来的是第二个问题

  1. 如何将一层内部的遍历花费化成图上的边权?

刚才已经说了,遍历时一定是在端点开始结束的,那么我们在层级间跃迁时其实只有四种情况,下面试加分析

我们设上一层点A,B(A靠左上,B靠右下,后面CD同理),下一层点C,D
我们从AB分别向CD连边,A-C连边代表上一层左上开始到下一层左上开始,那么权即应当为dis(A,B)+dis(B,C),意即上一层左上向右下遍历,在从上一层右下向上一层左上移动的花费,那么对应的A-D,B-C,B-D连边也类似。

这样一来,我们就基本完成了建图,我们将0,0设为起点,向第一层连边,权为距离,最后一层向终点连边,权为最后一层便利花费(两端点距离),再跑最短路即可。

大致思路说完了,那位大佬没有说具体代码实现细节,可能是觉得有手就行,但是我敲了好久,所以也具体说一说,,

用一个装有两个pair的pair数组来维护层级端点,用map映射层级和pair数组里对应的下标。对于输入,如果这个层级第一次出现,那么直接插入到pair后面。如果不是的话,比较一下是否可以作为端点,具体逻辑有点麻烦,有点类似于维护最大值和次大值那样,需要两次比较,可以具体看我的代码。

之后连边部分,s-第一层和最后一层-t需要单独拉出来连边,中间层的连边遍历map一层层的连即可,map是默认对key进行升序排序的,正好契合我们的level属性。

代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=800505;
	const int inf=0x3f3f3f3f3f3f3f3f;
    int n,m,k;
	map<int,int>mp;
    pair<pair<int,int>,pair<int,int> >pp[maxn];
    int p_cnt=1;
    int head[maxn];
	struct Edge{
		int to;
		int next;
		int w;
	} edge[maxn];
	int cnt;
	
 	int dis[maxn];
	void init(){
		cnt=0;
		memset(head,-1,sizeof(head));
		return ;
	}
	inline void add(int u,int v,int w){
        //cout<<u<< ' '<<v<<' '<<w<<endl;
		edge[cnt].next=head[u];
		edge[cnt].to=v;
		edge[cnt].w=w;
		head[u]=cnt;
		cnt++;
	}
	typedef pair<int,int> P;		
	void dij(int start){
		memset(dis,0x3f,sizeof(dis));
		priority_queue<P,vector<P>,greater<P> > q;
		dis[start]=0;
		q.push(P(0,start));
		while(!q.empty()){
			P p=q.top(); q.pop();
			int u=p.second;
			if(dis[u]<p.first)		continue;
			for(int i=head[u];i!=-1;i=edge[i].next){
				int v=edge[i].to;
				if(dis[v]>dis[u]+edge[i].w){
					dis[v]=dis[u]+edge[i].w;
					q.push(P(dis[v],v));
				}
			}
		}
		return ;
	}
    inline int getdis(int x1,int y1,int x2,int y2){
        return abs(x1-x2)+abs(y1-y2);
    }

	signed main(){
		IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
        init();
        cin>>n;
        for(int i=1;i<=n;i++){
            int x,y;
            cin>>x>>y;
            int rk=max(x,y);
            int no=mp[rk];
            if(no){
                int x1=pp[no].first.first,y1=pp[no].first.second;
                int x2=pp[no].second.first,y2=pp[no].second.second;
                if(x==rk){//x>=y,右下部分
                    if(y<=y2){
                        pp[no].second={x,y};
                        if(x1==rk)
                            pp[no].first.second=max(pp[no].first.second,y);
                    }
                    else if(x1==rk)
                        pp[no].first.second=max(pp[no].first.second,y);
                }
                else{
                    if(x<=x1){
                        pp[no].first={x,y};
                        if(y2==rk)
                            pp[no].second.first=max(pp[no].second.first,x);
                    }
                    else if(y2==rk)
                        pp[no].second.first=max(pp[no].second.first,x);
                }
            }
            else{
                mp[rk]=p_cnt;
                pp[p_cnt].first={x,y};
                pp[p_cnt++].second={x,y};
            }
        }
            
        int t=maxn-1;
        int ptcnt=1;
        int aaaa=0;
        auto it=mp.begin();
        int rk=it->first;
        int ptr=it->second;
        int last1x=pp[ptr].first.first,last1y=pp[ptr].first.second;
        int last2x=pp[ptr].second.first,last2y=pp[ptr].second.second;
        //cout<<"add 0,0"<<"to "<<last1x<<' '<<last1y<<' '<<"with value"<<getdis(0,0,last1x,last1y)<<endl;
        //cout<<"add 0,0"<<"to "<<last2x<<' '<<last2y<<' '<<"with value"<<getdis(0,0,last1x,last2y)<<endl;
        add(0,1,getdis(0,0,last1x,last1y));
        add(0,2,getdis(0,0,last2x,last2y));
        for(auto i:mp){
            if(!aaaa){
                aaaa=1;
                continue;
            }
            rk=i.first;
            ptr=i.second;
            int pt1x=pp[ptr].first.first,pt1y=pp[ptr].first.second;
            int pt2x=pp[ptr].second.first,pt2y=pp[ptr].second.second;
            //cout<<pt1x<<' '<<pt1y<<endl;
            //cout<<pt2x<<' '<<pt2y<<endl;
            add(ptcnt,ptcnt+2,getdis(last1x,last1y,last2x,last2y)+getdis(last2x,last2y,pt1x,pt1y));
            add(ptcnt,ptcnt+3,getdis(last1x,last1y,last2x,last2y)+getdis(last2x,last2y,pt2x,pt2y));
            add(ptcnt+1,ptcnt+2,getdis(last1x,last1y,last2x,last2y)+getdis(last1x,last1y,pt1x,pt1y));
            add(ptcnt+1,ptcnt+3,getdis(last1x,last1y,last2x,last2y)+getdis(last1x,last1y,pt2x,pt2y));
            ptcnt+=2;
            last1x=pt1x,last1y=pt1y;last2x=pt2x,last2y=pt2y;
        }
        it=mp.end();
        it--;
        add(ptcnt,t,getdis(last1x,last1y,last2x,last2y));
        add(ptcnt+1,t,getdis(last1x,last1y,last2x,last2y));
        dij(0);
        cout<<dis[t]<<endl;
	} 
						
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值