题目传送门
题意:
n行m列的棋盘。有k个宝藏,这些宝藏位于k个不同的位置。有q列,在这些特定列中你可以向上移动,当你所处列不属于这q列中时,不可以向上移动。开始时你位于左下角(1,1)这个位置,你只能向左,向右,以及在特定的q列中向上移动,不能向下移动。每移动一个位置消耗时间1,取宝藏不消耗时间。问你取完k个宝藏所需最小时间。
题解:
我用了dp的思想,但是并没有开数组,只用了四个状态。
第1个状态:取完上一行宝藏并处于上一行最左边宝藏位置所需最短时间。
第2个状态:取完上一行宝藏并处于上一行最右边宝藏位置所需最短时间。
第3个状态:取完当前行宝藏并处于当前行最左边宝藏位置所需最短时间。
第4个状态:取完当前行宝藏并处于当前行最右边宝藏位置所需最短时间。
列出这四个状态你会发现第3个状态可以由第1个状态或第2个状态转移而来,转移后取个最小值即可。
第4个状态可以由第1个状态或第2个状态转移而来,转移后取个最小值即可。
此时如果你不会转移状态,还需要发现一个规律,就是当前行想到达下一行,需要从当前位置移动到左边最近的特定列或右边最近的特定列,然后转移状态即可。
感受:
首先要想到这不是一个贪心题,因为当前行花费时间多一些到达特定列可能更优,我是想到这里发现是dp,并没有一眼dp。
由于没有一眼dp,所以注定了我比赛时很难做出这道题,因为比赛时间很短。
不过能不看题解做出这道题,对于现在的我也是不容易的,值得肯定。
此时1875分的我现在确实没有2000分实力,继续努力。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 2e5 + 5 ;
const int inf = 0x3f3f3f3f ;
int n , m , k , q ;
int l[maxn] , r[maxn] ;
set<int> st[maxn] ;
set<int> ::iterator it ;
struct node
{
int x , y ;
ll c ;
} a[10] ;
ll calc(node d , node e , node f , int z)
{
if(z == 1) return abs(f.y - d.y) + abs(f.y - e.y) ;
else return abs(e.y - d.y) + abs(f.y - e.y) ;
}
ll solve(node d , node e , node f , int z)
{
ll ans ;
if(d.x == e.x) ans = d.c + calc(d , e , f , z) ;
else
{
ans = 1e18 ;
if(l[d.y] != -1)
{
ll dis = d.y - l[d.y] , up = e.x - d.x ;
ll cost = calc(node{e.x , l[d.y]} , e , f , z) ;
ans = min(ans , d.c + dis + up + cost) ;
}
if(r[d.y] != inf)
{
ll dis = r[d.y] - d.y , up = e.x - d.x ;
ll cost = calc(node{e.x , r[d.y]} , e , f , z) ;
ans = min(ans , d.c + dis + up + cost) ;
}
}
return ans ;
}
void cal(int now)
{
a[3].x = a[4].x = now ;
a[3].y = *(st[now].begin()) ;
it = st[now].end() , it -- ;
a[4].y = *it ;
a[3].c = min(solve(a[1] , a[3] , a[4] , 1) , solve(a[2] , a[3] , a[4] , 1)) ;
a[4].c = min(solve(a[1] , a[3] , a[4] , 2) , solve(a[2] , a[3] , a[4] , 2)) ;
swap(a[1] , a[3]) , swap(a[2] , a[4]) ;
}
int main()
{
scanf("%d%d%d%d" , &n , &m , &k , &q) ;
for(int i = 1 ; i <= k ; i ++)
{
int x , y ;
scanf("%d%d" , &x , &y) ;
st[x].insert(y) ;
}
for(int i = 1 ; i <= m ; i ++) l[i] = -1 , r[i] = inf ;
for(int i = 1 ; i <= q ; i ++)
{
int x ;
scanf("%d" , &x) ;
l[x] = r[x] = x ;
}
for(int i = 2 ; i <= m ; i ++) l[i] = max(l[i] , l[i - 1]) ;
for(int i = m - 1 ; i >= 1 ; i --) r[i] = min(r[i] , r[i + 1]) ;
a[1].x = a[1].y = a[2].x = a[2].y = 1 , a[1].c = a[2].c = 0 ;
for(int i = 1 ; i <= n ; i ++)
{
if(st[i].size() == 0) continue ;
cal(i) ;
k -= st[i].size() ;
if(k == 0){printf("%lld\n" , min(a[1].c , a[2].c)) ; break ;}
}
return 0 ;
}