codeforces_D. Treasure Hunting_[DP+Binary Search]

http://codeforces.com/contest/1201/problem/D

The meaning of problems: a matrix of n rows and m columns, there are k Targets, starting from [1, 1], can only be moved one step to the four directions, and only moves upward in the q safecolumns, requires a minimum of how many times to get all the moving targets. ( 2 n- , m , K , Q 2 * 10 ^ . 5 , q≤m).

  

 Ideas:

Make two arrays: left and right. left[i] is the treasure in the leftmost position in row i (0 if there are no treasures in row ii). right[i] is the treasure in the rightmost cell in row ii (0 if there are no treasures in row ii).

We can simply take out rows where there is no treasure (and add 1 to the result if there are treasure above that line, because we have to move up there).

For every row, except the last, we have to leave that row at one of the safe columns. Let's notice that the last treasure we collect in the row will be either left[i] or right[i]. Let's take a look at both possibilities: If we collect the left[i] treasure last, we have to leave the row either going left or going right to the closest safe column, because going further wouldn't worth it (consider moving up earlier and keep doing the same thing at row i+1). The same is true for right[i]. For the first row, we start at the first column, we can calculate the moves required to go up the second row at the for cells. For all the other rows, we have 4 possibilities, and we have to calculate how many moves it takes to reach the row i+1 at the 4 possible columns. For the last row, we don't have to reach a safe column, we just have to collect all the treasures there. We can count the answer for the problem from the calculated results from the previous row. Time complexity: O(16n)

1. For the row exists treasure, the treasure finally get either the leftmost or rightmost;

2. Assuming finally got the far left, you can leave with this treasure about recent safecolumns; and finally got the rightmost situation the same way;

3. 对于第一行来说,若有宝藏,则获得最右边的宝藏后离开;所无宝藏,则通过离[1, 1]最近的safecolumn离开;

4. 对于其他行来说,最多可以有四种方式离开此行,最后一行不需要到达safecolumn,获得所有宝藏即可;

宝藏左右最近的safecolumn,可以通过binary search求得。

注意,若最左边的宝藏就在safecolumn上,则其左右最近的safecolumn都是此列。

#include <iostream>
#include <set>
#include <vector>
#include <algorithm> 
#include <queue>
using namespace std;
 
typedef long long LL;
 
int findSafe(vector<int>& safes, int x){
    int l = 0, r = safes.size()-1, ret;
    while(l <= r){
        int m = (l+r)>>1;
        if(safes[m] == x)
            return m;
        if(safes[m] > x)
            r = m - 1;
        else{
            ret = m;
            l = m + 1;
        }
    }
    return ret;
}
 
int dist(int layer, int p1, int p2, vector<int>& leftmost, vector<int>& rightmost, vector<int>& safecol){
    if(safecol[p1] > safecol[p2])
        swap(p1, p2);
    int d = safecol[p2] - safecol[p1];
    if(rightmost[layer] > safecol[p2])
        d += 2 * (rightmost[layer]-safecol[p2]);
    if(leftmost[layer] < safecol[p1])
        d += 2 * (safecol[p1]-leftmost[layer]);
    return d;
}
 
int main(){
    int n, k, m, q;
    cin>>n>>m>>k>>q;
    vector<int> leftmost(n+1, m+1), rightmost(n+1, 0), safecol{0};
    for(int i=0; i<k; i++){
        int row, col;
        cin>>row>>col;
        leftmost[row] = min(leftmost[row], col);
        rightmost[row] = max(rightmost[row], col);
    }
    for(int i=0; i<q; i++){
        int safe;
        cin>>safe;
        safecol.push_back(safe);
    }
 
    sort(safecol.begin(), safecol.end());
 
    while(leftmost[n] == m+1) n--;
 
    if(n==1){
        cout<<rightmost[1]-1<<endl;
        return 0;
    }
    vector<LL> now_step{0, 0, 0 ,0}, lst_step{0, 0, 0, 0};
    vector<int> lst_gate{-1, -1, -1, -1};
    if(rightmost[1] == 0){
        int rsafe = findSafe(safecol, 1);
        if(safecol[rsafe] < 1)
            rsafe++;
        lst_gate[0] = rsafe;
        lst_step[0] = safecol[rsafe]-1;
    }else{
        int lsafe = findSafe(safecol, rightmost[1]);
        //cout<<rightmost[1]<<"*"<<lsafe<<endl;
        lst_gate[0] = lsafe;
        lst_step[0] = 2*rightmost[1]-safecol[lsafe]-1;
        //cout<<"l10:"<<lst_step[0]<<endl;
        if(safecol[lsafe]<rightmost[1] && lsafe+1 < safecol.size()){
            lst_gate[1] = lsafe+1;
            lst_step[1] = safecol[lsafe+1]-1;
        }
    }
 
    for(int i=2; i<n; i++){
        if(leftmost[i] == m+1){
            for(int j=0; j<4; j++)
                lst_step[j]++; 
            continue;
        }else{
            vector<int> now_gate{-1, -1, -1, -1};
            int g1 = findSafe(safecol, leftmost[i]);
            int g2 = findSafe(safecol, rightmost[i]);
            //cout<<g1<<" "<<g2<<endl;
            now_gate[0] = g1;
            if(safecol[g1] < leftmost[i] && g1+1 < safecol.size())
                now_gate[1] = g1+1;
            now_gate[2] = g2;
            if(safecol[g2] < rightmost[i] && g2+1 < safecol.size())
                now_gate[3] = g2+1;
            for(int j=0; j<4; j++){
                now_step[j] = (4*1e5+1) * (4*1e5);
                for(int u=0; u<4; u++)
                    if(lst_gate[u]>0 && now_gate[j]>0){
                        int d = 1+dist(i,now_gate[j], lst_gate[u], leftmost, rightmost, safecol);
                        //cout<<now_gate[j]<<" "<<lst_gate[u]<<endl;
                        //cout<<"d:"<<i<<" "<<d<<endl;
                        //cout<<"ld:"<<" "<<lst_step[u]<<endl;
                        now_step[j] = min(now_step[j], lst_step[u]+d);
                    }
            }
            lst_step = now_step;
            lst_gate = now_gate;
        }
    }
    LL ret = (4*1e5+1) * (4*1e5);
    for(int u=0; u<4; u++)
        if(lst_gate[u] > 0){
            int d = 1+rightmost[n]-leftmost[n]+min(abs(rightmost[n]-safecol[lst_gate[u]]), abs(leftmost[n]-safecol[lst_gate[u]]));
            //cout<<"d:"<<" "<<d<<endl;
            //cout<<"lst_step:"<<lst_step[u]<<endl;
            ret = min(ret, lst_step[u]+d);
        }
    printf("%I64d\n", ret);
    return 0;
} 

 

Guess you like

Origin www.cnblogs.com/jasonlixuetao/p/11347677.html