Longest Mountain Itinerary
National Olympiad — National round (2019 season)
Submission name: theclimb.cpp
Problem summary
Choose a longest sequence of grid cells with strictly increasing altitude, with Manhattan distance at most D between consecutive cells. The required answer follows the official example and counts the cells in the itinerary.
Source clarification. The prose asks for days, but its described fifteen-cell itinerary is reported as 15, rather than fourteen transitions. This solution follows that demonstrated counting convention.
Input
N M D, then N rows of M altitudes.
Output
The maximum itinerary length, as in the official example.
Constraints
1 ≤ N,M,D ≤ 500; 1 ≤ altitude ≤ 1,000,000.
Worked example
An independently prepared example for this explanation.
Input
4 5 3 39 13 26 20 17 37 17 14 22 24 42 12 10 21 33 18 20 13 19 31
Output
15
Solution analysis
- Process cells by increasing altitude. The best itinerary ending at a cell is one plus the maximum previously computed value within Manhattan distance D. All cells of an equal altitude must be queried before any of them is inserted, preserving strict increase.
- Transform (r,c) into (u,v)=(r+c,r−c+M−1). Manhattan distance in the original grid equals max(|Δu|,|Δv|), so the allowed predecessor region is an axis-aligned square. An iterative two-dimensional segment tree supports point maximum updates and rectangle maximum queries.
- The official example describes fifteen visited altitudes from 10 to 42 and outputs 15. A single cell has value 1, so the code consistently counts cells. At height 13 both equal-height candidates see only lower altitudes and cannot extend one another.
Why it works
The coordinate identity converts exactly the permitted predecessor cells into the queried rectangle. Induct on increasing distinct altitude: every legal predecessor has already been inserted and every inserted cell has lower altitude. Therefore the rectangle maximum gives the longest legal predecessor itinerary, and appending the current cell yields the optimum for that endpoint. Equal-altitude batching prevents invalid transitions. Maximizing endpoint values gives the longest itinerary.
Watch out. Batch equal altitudes. The rotated grid contains unused parity positions; leaving them zero is correct. Count cells to match the official sample, despite its wording about days.
Commented C++17 solution
An independently written complete program with standard headers.
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
struct Cell{
int altitude,u,v,value;
};
class RangeMaximum{
int size;
int stride;
vector<int>tree;
int&at(int x,int y){
return tree[x*stride+y];
}
int queryRow(int x,int left,int right){
int answer=0;
for(left+=size,right+=size;left<=right;left/=2,right/=2){
if(left%2){
answer=max(answer,at(x,left++));
}
if(right%2==0){
answer=max(answer,at(x,right--));
}
}
return answer;
}
public:
explicit RangeMaximum(int bound):size(1){
while(size<bound){
size*=2;
}
stride=2*size;
tree.assign(stride*stride,0);
}
void update(int x,int y,int value){
x+=size;
y+=size;
at(x,y)=max(at(x,y),value);
for(int column=y/2;column;column/=2){
at(x,column)=max(at(x,2*column),at(x,2*column+1));
}
for(x/=2;x;x/=2){
for(int column=y;column;column/=2){
at(x,column)=max(at(2*x,column),at(2*x+1,column));
}
}
}
int query(int x1,int x2,int y1,int y2){
x1=max(x1,0);
y1=max(y1,0);
x2=min(x2,size-1);
y2=min(y2,size-1);
int answer=0;
for(x1+=size,x2+=size;x1<=x2;x1/=2,x2/=2){
if(x1%2){
answer=max(answer,queryRow(x1++,y1,y2));
}
if(x2%2==0){
answer=max(answer,queryRow(x2--,y1,y2));
}
}
return answer;
}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,distance;
cin>>n>>m>>distance;
vector<Cell>cells;
for(int r=0;r<n;r++){
for(int c=0;c<m;c++){
int altitude;
cin>>altitude;
cells.push_back({altitude,r+c,r-c+m-1,0});
}
}
sort(cells.begin(),cells.end(),[](const Cell&a,const Cell&b){
return a.altitude<b.altitude;
});
RangeMaximum maximum(n+m-1);
int answer=0;
for(int begin=0;begin<static_cast<int>(cells.size());){
int end=begin;
while(end<static_cast<int>(cells.size())&&cells[end].altitude==cells[begin].altitude){
Cell&cell=cells[end];
cell.value=
1+maximum.query(cell.u-distance,cell.u+distance,cell.v-distance,cell.v+distance);
answer=max(answer,cell.value);
end++;
}
// Insert only after every cell of this height has been evaluated.
for(int i=begin;i<end;i++){
maximum.update(cells[i].u,cells[i].v,cells[i].value);
}
begin=end;
}
cout<<answer<<'\n';
}