codeforces1201D 2000分dp

题目传送门

题意:

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 ;
}
发布了187 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104072438
今日推荐