题目
【题目描述】
小$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;
}