zoukankan      html  css  js  c++  java
  • [Codeforces 1201D]Treasure Hunting(DP)

    [Codeforces 1201D]Treasure Hunting(DP)

    题面

    有一个n*m的方格,方格上有k个宝藏,一个人从(1,1)出发,可以向左或者向右走,但不能向下走。给出q个列,在这些列上可以向上走,其他列不能向上走。可以重复经过同一个点。求从(1,1)出发,经过所有宝藏的最短路径长度

    (n,m,k,q leq 2 imes 10^5)

    分析

    贪心考虑,我们应该按照行一层一层的走。每一行应该从最左的宝藏走到最右的宝藏,或者从最右的宝藏走到最左的宝藏,然后找最近的一个可以向上走的列,走到另一行。

    (dp[i][0/1])表示当前在第i行最左边的宝藏,或者最右边的宝藏,已经走完该行所有宝藏的最短距离

    那么只要记录上一个有宝藏的行pre,分类讨论一下四种转移

    (dp[i][0])需要先走到第i行的最右端,再从第i行的最右端走到最左端

    (dp[i][1])需要先走到第i行的最右端,再从第i行的最右端走到最左端

    另一个问题是如何计算(x1,y1)到(x2,y2)的距离。我们预处理在第i列左边最近的可以向上走的列lb[i],和最右边最近的列rb[i],那么我们走的列一定是lb[y1],lb[y2],rb[y1],rb[y2]四列中的一列。

    如下图,假设y1<y2,走lb[y1]左侧的一列显然不如lb[y1]优,走rb[y2]右侧的一列显然不如rb[y2]优,而走rb[y1],lb[y2]中间的列和直接走rb[y1],lb[y2]一样

    把经过这四列向上的距离统计出来就可以了

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define maxn 200000 
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    int n,m,k,q;
    vector<int>a[maxn+5];
    int lb[maxn+5],rb[maxn+5];
    int col[maxn+5]; 
    ll dp[maxn+5][2];//走到最左侧/最右侧结束 
    ll calc(int y1,int y2,int line){//从y1到第line列,走上去,再到y2
    	return (ll)abs(y1-line)+abs(y2-line);
    }
    ll dist(int x1,int y1,int x2,int y2){
    	ll distx=abs(x1-x2);
    	ll disty=min(min(calc(y1,y2,lb[y1]),calc(y1,y2,lb[y2])),min(calc(y1,y2,rb[y1]),calc(y1,y2,rb[y2])));
    	return distx+disty;
    }
    int main(){
    	int x,y;
    	scanf("%d %d %d %d",&n,&m,&k,&q);
    	for(int i=1;i<=k;i++){
    		scanf("%d %d",&x,&y);
    		a[x].push_back(y); 
    	}
    	for(int i=1;i<=q;i++){
    		scanf("%d",&col[i]);
    	}
    	sort(col+1,col+1+q);
    	col[0]=-INF;
    	col[q+1]=INF;
    	for(int i=1;i<=m;i++){
    		lb[i]=col[lower_bound(col+1,col+1+q,i)-col-1];
    		rb[i]=col[lower_bound(col+1,col+1+q,i)-col];
    	} 
    	
    	sort(a[1].begin(),a[1].end());
    	if(!a[1].empty()){
    		dp[1][0]=dp[1][1]=a[1].back()-1;
    		a[1][0]=a[1].back();
    		//第一行必须从最后一个关键点往上走,否则就走不完所有关键点 
    	}else{
    		a[1].push_back(1);
    		dp[1][0]=dp[1][1]=0;
    	}
    	int last=1;
    	for(int i=2;i<=n;i++){
    		if(!a[i].empty()){
    			sort(a[i].begin(),a[i].end());
    			ll l_to_l=dp[last][0]+dist(last,a[last].front(),i,a[i].front());//从pre行最左侧走到i行最左侧
    			ll r_to_l=dp[last][1]+dist(last,a[last].back(),i,a[i].front()); //从pre行最右侧走到i行最左侧
    			ll l_to_r=dp[last][0]+dist(last,a[last].front(),i,a[i].back());//从pre行最左侧走到i行最右侧
    			ll r_to_r=dp[last][1]+dist(last,a[last].back(),i,a[i].back());//从pre行最右侧走到i行最右侧
    			ll now=a[i].back()-a[i].front();//走完当前行的代价 
    			dp[i][0]=min(l_to_r,r_to_r)+now;//先走到最右侧,最后到最左侧 
    			dp[i][1]=min(l_to_l,r_to_l)+now;//先走到最左侧,最后到最右侧 
    			last=i;
    		}
    	} 
    	printf("%I64d
    ",min(dp[last][0],dp[last][1]));
    	
    }
    
    
  • 相关阅读:
    管理业务IT从业人员转型生产管理1
    坐标系基准面地图投影系列介绍(二)_ 地理坐标系
    优化UVA11401(Triangle Counting)
    最小匹配hdu 3991 Harry Potter and the Present II
    解析xml——采用Jdom与dom4J方式写入xml文档
    解析xml——采用Jdom与dom4J方式读xml文档
    解析xml笔记总纲
    使用zoom.js 给博客园的图片添加点击图片放大功能
    Hello China V1.75版本运行截图
    基于visual c++之windows核心编程代码分析(26)实现文件关联
  • 原文地址:https://www.cnblogs.com/birchtree/p/11301444.html
Copyright © 2011-2022 走看看