题目
【题目描述】
小$A$决定开始一场奇妙的徒步旅行,旅行地图可以看成是一个平面直角坐标系,小$A$从家$O(0,0)$出发,每一步移动只能由他此时所在的位置$(x,y)$走到以下四个坐标之一:$(x-1,y),(x,y-1),(x+1,y),(x,y+1)$。现在有$N$个旅游景点,第$i$个旅游景点位置为$(x_i,y_i)$。 由于世界如此之大,整个旅行地图被分成了多个不同的气候区,某个景点$(x_i,y_i)$的气候区$C_i=max(x_i,y_i)$。小$A$想要更好的了解这个世界使得他这次徒步旅行更有意义,所以他想要去气候区$i+1$旅行当且仅当访问完气候区$i$的所有旅游景点。当他访问完所有的景点时,他会回到家里。 小$A$想让你帮他设计出一条旅游路线使得他移动的步数最少,因为徒步旅行还是比较累的……
【输入格式】
第一行输入一个整数$N$,表示旅游景点数量。
接下来$N$行,每行一个整数对$(x_i,y_i)$代表第$i$个景区的位置。
【输出格式】
仅一行,表示小$A$完成旅行所需移动的最少步数。
【数据范围】
对于所有测试数据:
$1≤n≤3 * 10^5$
$0≤h≤10^9$
$0≤x,y≤10^9$
题解
【做题经历】
这道题看到有四个方向,又是在一个网格中走路,首先我想到的就是搜索......
看了看数据范围,然后居然又想到剪枝或者是一个记忆化......
然后,$dp$思路就出来了
刚开始的$dp$思路:
定义状态$dp_{ij}$:已经访问了第$i$个气候区的其中$j$个
这个状态转移似乎很好想,但是感觉有些别扭
比如说你的上一层状态是从哪一个点转移过来的,这还是要枚举
然后就是你已经访问当前气候区的哪些点了,这个也是不知道的
所以说,这个状态定义得是有问题的
【正解】
想了一会儿,发现了一个关键的点:
每一个气候区在网格中都是一个拐角的形状,就像这样
当然这不是我说的关键的点
而每次访问完一个气候区,总是在这个拐角的最左边或者是最下边
那么我们就可以再新定义一个状态$dp[i][0|1]$:在访问完第i个气候区之后,是在最左边$(j==0)$还是最下边$(j==1)$
将输入的数据按照气候区从小到大排序(当然还有很多细节,这个交给你自己想或者是$py$一下代码)
然后处理出一个$l$数组与一个$r$数组,其中
$l[i]$:第i个气候区最左边的点的下标
$r[i]$:第i个气候区最右边的点的下标
那么状转为$$dp[i][0]=Min(dp[i-1][0]+dis(l[i-1],r[i]),dp[i-1][1]+dis(r[i-1],r[i]))+dis(r[i],l[i])$$
$$dp[i][1]=Min(dp[i-1][0]+dis(l[i-1],l[i]),dp[i-1][1]+dis(r[i-1],l[i]))+dis(l[i],r[i])$$
状转都出来了,为什么不上代码?
#include<bits/stdc++.h> using namespace std; #define int long long #define cg (c=getchar()) inline int qread(){ int x,f=1;char c; while(cg<'0'||'9'<c)if(c=='-')f=-1; for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48)); return x*f; } #undef cg template<class T>inline T Max(const T x,const T y){return x>y?x:y;} template<class T>inline T Min(const T x,const T y){return x<y?x:y;} template<class T>inline T fab(const T x){return x>0?x:-x;} const int MAXN=3e5; struct node{ int x,y,c; node(){x=y=c=0;} node(const int X,const int Y):x(X),y(Y),c(Max(X,Y)){} node(const int X,const int Y,const int C):x(X),y(Y),c(C){} bool operator<(const node& a){ if(c!=a.c)return c<a.c; if(x!=a.x)return x<a.x; return y<a.y; } }dat[MAXN+5]; inline int dis(const int i,const int j){return fab(dat[i].x-dat[j].x)+fab(dat[i].y-dat[j].y);} int hasind; int dp[MAXN+5][2]; int l[MAXN+5],r[MAXN+5]; signed main(){ int N=qread(); for(int i=1,a,b;i<=N;++i){a=qread(),b=qread();dat[i]=node(a,b);} sort(dat+1,dat+N+1); l[hasind]=0; for(int i=1,pre=0;i<=N;++i){ if(dat[i].c^pre){ r[hasind]=i-1; pre=dat[i].c; l[++hasind]=i; } dat[i].c=hasind; } r[hasind]=N; for(int i=1;i<=hasind;++i){ dp[i][0]=Min(dp[i-1][0]+dis(l[i-1],r[i]),dp[i-1][1]+dis(r[i-1],r[i]))+dis(r[i],l[i]); dp[i][1]=Min(dp[i-1][0]+dis(l[i-1],l[i]),dp[i-1][1]+dis(r[i-1],l[i]))+dis(l[i],r[i]); } printf("%lld\n",Min(dp[hasind][0]+dis(l[hasind],0),dp[hasind][1]+dis(r[hasind],0))); return 0; }