Manhattan tourist - algorithm

In my algorithms and datastructures class I have been asked to implement the Manhattan tourist problem using dynamic programming.
I have come to a solution using a combination of dynamic programming and recursive calls, but I seem to get "Time limit exceeded" when putting it to the test on CodeJudge. I haven't been able to figure out why my code isn't fast enough. Any takers?
Best regards.
Description of the problem:
Your are helping the tourist guide company "Manhattan Tourists", that are arranging
guided tours of the city. They want to find a walk between two points on the map that is both interesting and short. The map is a square grid graph. The square grid graph has n rows with n nodes in each row. Let node vi,j denote the jth node on row i. For 1≤I<n and for 1≤j≤n node vi,j is connected to vi+1, j. And for 1≤i≤n and for 1 ≤ j < n node vi,j is connected to vi,j+1. The edges have non-negative edge weights that indicate how interesting that street is. See the graph below for an example of a 5 × 5 grid graph.
They want to find a short interesting walk from the upper left corner (s = v1,1) to the lower right corner (t = vn,n). More precisely, they want to find a path with the possible smallest number of edges, and among all paths with this number of edges they want the path with the maximum weight (the weight of a path is the sum of weights on the path).
All shortest paths have 2n − 2 edges and go from s to t by walking either down or right in each step. In the example below two possible shortest paths (of length 8) are indicated. The dashed path has weigth 38 and the dotted path has weight 30.
Let W [i, j] be the maximal weight you can get when walking from s to vi, j walking either down or right in each step. Let D[i, j] be the weight of the edge going down from vi, j and let R[i, j] be the weight of the edge going right from vi,j.
Description on CodeJudge:
Exercise
Before you can solve this exercise, you must first read, understand and (partly) solve the problem Manhattan Tourists described on the weekplan.
Your task here is to implement your solution. Read the input/output specification below and look at the sample test data in order to learn how to read the input and write the output.
Input format
Line 1: The integer n (1<= n <= 1000).
Line 2..n+1: the n rows of R, each consisting of n-1 integers separated by space.
Line n+2..2n: the n-1 rows of D, each consisting of n integers separated by space.
Output format:
Line 1: The maximum interest score of a shortest walk.
Heres my code so far:
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
int n = console.nextInt();
int[][] R = new int[n][n-1];
int[][] D = new int[n-1][n];
for(int i = 0; i < n; i++) {
for(int j = 0; j < n-1; j++) {
R[i][j] = console.nextInt();
}
}
for(int i = 0; i < n-1; i++) {
for(int j = 0; j < n; j++) {
D[i][j] = console.nextInt();
}
}
System.out.println(opt(R, D, n, n-1, n-1));
}
public static int opt(int[][]R, int[][]D, int n, int i, int j) {
int[][] result = new int[n][n];
if(i==0 && j==0) {
if(result[i][j] == 0) {
result[i][j] = 0;
}
return result[i][j];
} else if(i == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i,j-1) + R[i][j-1];
}
return result[i][j];
}else if(j == 0) {
if(result[i][j] == 0) {
result[i][j] = opt(R,D,n,i-1,j) + D[i-1][j];
}
return result[i][j];
}else if(result[i][j] == 0) {
result[i][j] = max(opt(R, D, n, i, j-1) + R[i][j-1],opt(R, D, n, i-1, j) + D[i-1][j]);
}
return result[i][j];
}
public static int max(int i, int j) {
if(i > j) {
return i;
}
return j;
}
}

Why a recursion?
The topmost row can be traversed horizontally only. So, for each vertex in the first row the total weight is a sum of weights of branches to the left. You can compute all of them in a single loop as a running total across the row.
For each next row the total weight of the first vertex is a weight of the vertex above it plus the weight of the branch between them. And the total weight of each next vertex in the row is the bigger one from two possible when coming from above or from left.
All that can be computed iteratively with two nested loops.

Related

find the loop in undirected graph with maximal number of nodes and upper bound of length

Here is an algo coding question:
There are stones in two dimension array, and given a known length rope, determine what's the maximum number of stones you can use the rope to surround? (the rope must be enclosed).
I think the question is to find the loop in undirected graph with maximal number of nodes and upper bound of length and BFS seems a good way, but seems still NP-hard.
EDIT:
Here is an example: 5 stones in a plane. I listed three surroundings: black, red and blue. The length of each surrounding is the sum of its strings.
The question is equivalent to select n of N points by order x1,...xn with its sum = d(x1,x2)+d(x2,x3)+...+d(xn-1,xn)+d(xn,x1) <= C. Here n>1. What's the maximal n?
Solution:
After some hints, I think it is nothing about the undirected graph. It is similar to the problem that selecting some elements from a given array equal to a target sum. But the difference is that the result depends on the selecting order, therefore we cannot select by the order from the given array. Here is my solution. But there are still a lot of duplicate searches, for example, if black loop is eligible, we have to search 4 times from each of its nodes.
double d(const vector<double> a, const vector<double> b)
{
return sqrt((a[0] - b[0])*(a[0] - b[0]) + (a[1] - b[1])*(a[1] - b[1]));
}
//recursive
//out: current legal elements; sum: the sum of out; ct: the maximal number up to now
//visited: record the elements in out has been visited to void duplicate visit
//res: help to check the result in debuge
void help(vector<vector<double>> array, double target, vector<vector<double>> &out, int &sum, int & ct, vector<vector<vector<double>>> &res, vector<bool> &visited)
{
for (int i = 0; i < array.size(); i++) //since it depends on the order of selection, we have to search from begining. visited helps us to skip the element already been in out.
{
if (visited[i]) continue;
else if (out.empty()) {out.push_back(array[i]); visited[i] = true;}
else
{
vector<double> last = out.back(), first = out.front();
if (sum + d(array[i], last) + d(array[i], first) <= target)
{
out.push_back(array[i]);
sum += d(array[i], last);
ct = max(ct, (int)out.size());
visited[i] = true;
help(array, target - d(array[i], last), out, sum, ct, res, visited);
out.pop_back();//when the loop is over at this level, we should return to the state of previous level
sum -= d(array[i], last);
visited[i] = false;
}
else
{
res.push_back(out);
return;
}
}
}
}
int Path(vector<vector<double>> array, double target) {
int sum = 0, ct = 0;
vector<vector<double>> out;
vector<vector<vector<double>>> res;
vector<bool> visited(array.size(), false);
help(array, target, out, sum, ct, res, visited);
return ct;
}

minimum cost to reach destination through tunnels

Recently I faced a problem in the interview and not able to answer it. Any help will be appreciated.
Given a two dimensional grid (1 <= row <= 10^9 && 1 <= col <= 10^9) and starting and ending coordinates. we can only go to the four adjacent cells and it cost 1 unit. we also have N tunnels (1 <= N <= 200) whose starting and ending coordinates are given and if we go through the tunnel it costs k unit (1 <= k <= 10^9).
Note: It is not necessary to take tunnels but if we take one it costs k unit of energy per tunnel taken.
we have to find the minimum cost to reach the destination from the starting coordinate.
starting coordinate (1 <= sx, sy <= 10^9)
destination coordinate (1 <= fx, fy <= 10^9)
The problem needs to be transposed into a graph with a weight given to each vertex. Once we have done that, we can use the Dijkstra algorithm to find the shortest path.
Solving the problem thus boils down to transposing the problem into a graph with weighted vertices.
We can go from any cell to any other cell without going through a tunnel. The cost is then the manhattan distance. When the coordinate of a cell c1 is (x1,y1) and another cell c2 is (x2,y2), the manhattan distance between c1 and c2 is d=abs(x2-x1)+abs(y2-y1).
The nodes of the graph will correspond to the starting cell, the final cell, and every tunnel exit cells. The number of nodes in the graph is 2 + n where n is the number of tunnels.
There is a vertex between every node. The weight of a vertex to the final node is simply the manhattan distance. The weight of a vertex to a tunnel exit node is the manhattan distance to the tunnel entry cell plus the weight k associated to the tunnel.
This yields a graph that we can now solve using the Dijkstra algorithm to find the shortest path.
As chmike mentioned, the question can first be transformed into a graph. Then Djikstra's algorithm for finding shortest paths can be used. Here's is my code -
#include<bits/stdc++.h>
using namespace std;
#define int long long int
const int N = 402;
int dp[N][N];
pair<int,int> g[N];
int dist[N];
bool vis[N];
int32_t main(){
int k,a,b,c,d,n,p,q,r,s,index,nodes,val;
cin>>k>>a>>b>>c>>d>>n;
index = 2;
nodes = 2*n+1;
for(int i=1;i<=nodes;i++)
dist[i] = INT_MAX;
memset(vis,false,sizeof(vis));
memset(dp,-1,sizeof(dp));
for(int i=0;i<=nodes;i++)
dp[i][i] = 0;
g[0] = {a,b};
g[1] = {c,d};
dp[0][1] = dp[1][0] = abs(a-c)+abs(b-d);
for(int i=0;i<n;i++){
cin>>p>>q>>r>>s;
dp[index][index+1] = k;
dp[index+1][index] = k;
g[index] = {p,q};
g[index+1] = {r,s};
for(int j=0;j<index;j++){
val = abs(p-g[j].first)+abs(q-g[j].second);
dp[j][index] = dp[index][j] = val;
val = abs(r-g[j].first)+abs(s-g[j].second);
dp[j][index+1] = dp[index+1][j] = val;
}
index += 2;
}
for(int i=0;i<=nodes;i++){
int v = -1;
for(int j=0;j<=nodes;j++){
if(!vis[j] && (v == -1 || dist[j] < dist[v]))
v = j;
}
if(dist[v] == INT_MAX)
break;
vis[v] = true;
for(int j=0;j<=nodes;j++)
dist[j] = min(dist[j], dist[v]+dp[v][j]);
}
cout<<dist[1];
return 0;
}
you can use dynamic programming
#include <bits/stdc++.h>
using namespace std;
#define type long long
int main()
{ //k i sost of travelling through tunnel
//sx and sy are starting coordinates
//fx and fy are ending coordinates
//n are number of tunnels
int k, sx, sy, fx ,fy,n;
cin>>k>>sx>>sy>>fx>>fy>>n;
vector<vector<int>> arr(n, vector<int>(4,0));
map<pair<int, int> , pair<int,int>> mp;
//taking inputof tunnel elements and storing it in a map
for(int i=0; i<n; i++)
{
for(int j=0; j<4; j++)
cin>>arr[i][j];
pair<int,int> a,b;
a= pair<int,int> (arr[i][0], arr[i][1]);
b= pair<int, int> (arr[i][2], arr[i][3]);
mp[a] = b;
mp[b] =a;
}//cin the elements
//function
vector<vector<type>> dp (fx+1, vector<type>(fy+1,LONG_LONG_MAX));
dp[fx][fy] =0; //end
for(int i= fx; i>=0; i--)
{
for(int j= fy; j>=0; j--)
{
//go down
if(j+1< fy)
{
dp[i][j] = min(dp[i][j] , dp[i][j+1]+1 );
}
//go right
if(i+1< fx)
{
dp[i][j] = min(dp[i][j] , dp[i+1][j]+1 );
}
//tunnel
if(mp.find(pair<int, int> (i,j))!= mp.end())
{
pair<int, int> temp= mp[pair<int, int> (i,j)];
int a= temp.first, b= temp.second;
if(dp[a][b]!= LONG_LONG_MAX)
{
dp[i][j] = min(dp[i][j] , dp[a][b]+ k );
}
}
}
}//
cout<<dp[sx][sy]<<'\n';
}
here i have used dp
the array dp is 2-d matrix that saves the cost to reach fx, fy.
we start from bottom up approach, at each cell we find the minimum cost to reach the end.
we check the cost to reach by stepping 1 cell downward i.e. from dp[i][j] to dp[i][j+1] .
then we check the right cell by dp[i+1][j]
we see if tunnel is present.

Maximum Matching in Graph

I have 2D Array where each element is O or Non Zero, I have to collect all 1 elements. For Doing so I will hire a workers
`0` - dead coconut tree
`Non Zero` - living tree
A worker starts harvesting at a living tree and continues harvesting along a straight line of trees in one of the four cardinal directions (i.e., north, south, east, or west). A worker stops harvesting coconuts when one of the following conditions is satisfied
The worker comes across a dead coconut tree.
The worker hits the edge of the plantation (i.e., there are no more
harvestable trees in that direction).
For Example Array Look Like This:
So Minimum 4 workers are required
Question:
I was shocked when i came to know this is Maximum Matching Problem , i had no idea how is it so.
Following is Code which Mark the Horizontal and Vertical Strips with a index
m=1;
for (int i=0; i<r; i++) {
int j = 0;
while (j < c && a[i][j] < m) j++;
while (j < c) {
while (j < c && a[i][j] >= m) {
h[i][j] = hcnt;
j++;
}
hcnt++;
while (j < c && a[i][j] < m) j++;
}
}
int vcnt = 0;
for (int j=0; j<c; j++) {
int i = 0;
while (i < r && a[i][j] < m) i++;
while (i < r) {
while (i < r && a[i][j] >= m) {
v[i][j] = vcnt;
i++;
}
vcnt++;
while (i < r && a[i][j] < m) i++;
}
}
So my Question is How to add edges and why it's maximum matching problem and can someone explain the intuition behind this why maximum matching works on this problem.
Original Question
Following Code Add the edges. I hade no idea how we are adding the edges and why is it working
int s = 0, t = hcnt + vcnt + 1;
for (int i=0; i<hcnt; i++) addEdge(s, i+1, 1);
for (int i=0; i<r; i++) {
for (int j=0; j<c; j++) if(a[i][j] >= m) {
addEdge(1 + h[i][j], 1 + hcnt + v[i][j], 1);
}
}
for (int i=0; i<vcnt; i++) addEdge(1 + hcnt + i, t, 1);
The idea is as follows: we need to take some vertical and horizontal lines in such a way that all trees are covered and the number of picked lines is minimized.
Each cell with a tree becomes an edge in the graph. Each vertical line becomes a vertex in the left part of the graph and each horizontal line becomes a vertex in the right part. Now the problem is equivalent to finding a vertex cover in this graph. The size of the maximum matching is equal to the size of the vertex cover in any bipartite graph (it's a more or less well-known theorem).

How can I develop the exact recurrence for this?

N buildings are built in a row, numbered 1 to N from left to right.
Spiderman is on buildings number 1, and want to reach building number N.
He can jump from building number i to building number j iff i < j and j-i is a power of 2 (1,2,4, so on).
Such a move costs him energy |Height[j]-Height[i]|, where Height[i] is the height of the ith building.
Find the minimum energy using which he can reach building N?
Input:
First line contains N, number of buildings.
Next line contains N space-separated integers, denoting the array Height.
Output:
Print a single integer, the answer to the above problem.
So, I thought of something like this:
int calc(int arr[], int beg, int end, )
{
//int ans = INT_MIN;
if (beg == end)
return 0;
else if (beg > end)
return 0;
else
{
for (int i = beg+1; i <= end; i++ ) // Iterate over all possible combinations
{
int foo = arr[i] - arr[beg]; // Check if power of two or not
int k = log2(foo);
int z = pow(2,k);
if (z == foo) // Calculate the minimum value over multiple values
{
int temp = calc(arr,i,end);
if (temp < ans)
temp = ans;
}
}
}
}
The above is a question that I am trying to solve and here is the link: https://www.codechef.com/TCFS15P/problems/SPIDY2
However, the above recurrence is not exactly correct. Do I have to pass in the value of answer too in this?
We can reach nth building from any of (n-2^0),(n-2^1),(n-2^2)... buildings. So we need to process the buildings starting from 1. For each building i we calculate cost for getting there from any of earlier building j where i-j is power of 2 and take the minimum cost.
int calc(int arr[],int dp[],int n) {
// n is the target building
for(int i=1; i<=n; i++) dp[i]=LLONG_MAX; //initialize to infinity
dp[1]=0; // no cost for starting building
for(int i=2; i<=n; i++) {
for(int j=1; i-j>=1; j*=2) {
dp[i]=min(dp[i], dp[i-j]+abs(arr[i]-arr[i-j]));
}
}
return dp[n];
}
Time complexity is O(n*log(n)).
First, you are doing the check for a power of 2 on the wrong quantity. The jumps have to be between buildings that are separated in index by a power of 2, not that differ in height (which is what you are checking).
Second, the recursion should be formulated in terms of the cost of the first jump and the cost of the remaining jumps (obtained by a recursive call). You are looking for the minimum cost over all legal first jumps. A first jump is legal if it is to a building that is at an index less than N and also a power of 2 in index away from the current start.
Something like this should work:
int calc(int arr[], int beg, int end)
{
if (beg == end)
return 0;
else if (beg > end)
throw an exception
int minEnergy = INFINITY;
for (int i = 1; // start with a step of 1
beg + i <= end; // test if we'd go too far
i <<= 1) // increase step to next power of 2
{
int energy = abs(arr[beg + i] - arr[beg]) // energy of first jump
+ calc(arr, beg + i, end); // remaining jumps
if (energy < minEnergy) {
minEnergy = energy;
}
}
return minEnergy;
}
The efficiency of this search can be greatly improved by passing the minimum energy obtained so far. Then if abs(arr[beg + i] - arr[beg]) is not less than that quantity, there's no need to do the recursive call, because whatever is found will never be smaller. (In fact, you can cut off the recursion if abs(arr[beg + i] - arr[beg]) + abs(arr[end] - arr[beg + i]) is not smaller than the best solution so far, because Spiderman will have to at least spend abs(arr[end] - arr[beg + i]) after getting to building beg + i.) Adding this improvement is left as an exercise. :)

Lexographically smallest path in a N*M grid

I came across this in a recent interview.
We are given a N*M grid consisting of numbers and a path in the grid is the nodes you traverse.We are given a constraint that we can only move either right or down in the grid.So given this grid, we need to find the lexographically smallest path,after sorting it, to reach from top left to bottom right point of the grid
Eg. if grid is 2*2
4 3
5 1
then lexographically smallest path as per the question is "1 3 4".
How to do such problem? Code is appreciated. Thanks in advance.
You can use Dynamic programming to solve this problem. Let f(i, j) be the smallest lexicographical path (after sorting the path) from (i, j) to (N, M) moving only right and down. Consider the following recurrence:
f(i, j) = sort( a(i, j) + smallest(f(i + 1, j), f(i, j + 1)))
where a(i, j) is the value in the grid at (i, j), smallest (x, y) returns the smaller lexicographical string between x and y. the + concatenate two strings, and sort(str) sorts the string str in lexical order.
The base case of the recurrence is:
f(N, M) = a(N, M)
Also the recurrence change when i = N or j = M (make sure that you see that).
Consider the following code written in C++:
//-- the 200 is just the array size. It can be modified
string a[200][200]; //-- represent the input grid
string f[200][200]; //-- represent the array used for memoization
bool calculated[200][200]; //-- false if we have not calculate the value before, and true if we have
int N = 199, M = 199; //-- Number of rows, Number of columns
//-- sort the string str and return it
string srt(string &str){
sort(str.begin(), str.end());
return str;
}
//-- return the smallest of x and y
string smallest(string & x, string &y){
for (int i = 0; i < x.size(); i++){
if (x[i] < y[i]) return x;
if (x[i] > y[i]) return y;
}
return x;
}
string solve(int i, int j){
if (i == N && j == M) return a[i][j]; //-- if we have reached the buttom right cell (I assumed the array is 1-indexed
if (calculated[i][j]) return f[i][j]; //-- if we have calculated this before
string ans;
if (i == N) ans = srt(a[i][j] + solve(i, j + 1)); //-- if we are at the buttom boundary
else if (j == M) ans = srt(a[i][j] + solve(i + 1, j)); //-- if we are at the right boundary
else ans = srt(a[i][j] + smallest(solve(i, j + 1), solve(i + 1, j)));
calculated[i][j] = true; //-- to fetch the calculated result in future calls
f[i][j] = ans;
return ans;
}
string calculateSmallestPath(){
return solve(1, 1);
}
You can apply a dynamic programming approach to solve this problem in O(N * M * (N + M)) time and space complexity.
Below I'll consider, that N is the number of rows, M is the number of columns, and top left cell has coordinates (0, 0), first for row and second for column.
Lets for each cell store the lexicographically smallest path ended at this cell in sorted order. The answer for row and column with 0 index is trivial, because there is only one way to reach each of these cells. For the rest of cells you should choose the smallest path for top and left cells and insert the value of current cell.
The algorithm is:
path[0][0] <- a[0][0]
path[i][0] <- insert(a[i][0], path[i - 1][0])
path[0][j] <- insert(a[0][j], path[0][j - 1])
path[i][j] <- insert(a[i][j], min(path[i - 1][j], path[i][j - 1])
If no number is repeated, this can be achieved in O (NM log (NM)) as well.
Intuition:
Suppose I label a grid with upper left corner (a,b) and bottom right corner (c,d) as G(a,b,c,d). Since you've to attain the lexicographically smallest string AFTER sorting the path, the aim should be to find the minimum value every time in G. If this minimum value is attained at, let's say, (i,j), then G(i,b,c,j) and G(a,j,i,d) are rendered useless for the search of our next min (for the path). That is to say, the values for the path we desire would never be in these two grids. Proof? Any location within these grids, if traversed will not let us reach the minimum value in G(a,b,c,d) (the one at (i,j)). And, if we avoid (i,j), the path we build cannot be lexicographically smallest.
So, first we find the min for G(1,1,m,n). Suppose it's at (i,j). Mark the min. We then find out the min in G(1,1,i,j) and G(i,j,m,n) and do the same for them. Keep continuing this way until, at the end, we have m+n-1 marked entries, which will constitute our path. Traverse the original grid G(1,1,m,n) linearly and the report the value if it is marked.
Approach:
To find the min every time in G is costly. What if we map each value in the grid to it's location? - Traverse the grid and maintain a dictionary Dict with the key being the value at (i,j) and the value being the tuple (i,j). At the end, you'll have a list of key value pairs covering all the values in the grid.
Now, we'll be maintaining a list of valid grids in which we will find candidates for our path. The first valid grid will be G(1,1,m,n).
Sort the keys and start iterating from the first value in the sorted key set S.
Maintain a tree of valid grids, T(G), such that for each G(a,b,c,d) in T, G.left = G(a,b,i,j) and G.right = G(i,j,c,d) where (i,j) = location of min val in G(a,b,c,d)
The algorithm now:
for each val in sorted key set S do
(i,j) <- Dict(val)
Grid G <- Root(T)
do while (i,j) in G
if G has no child do
G.left <- G(a,b,i,j)
G.right <- G(i,j,c,d)
else if (i,j) in G.left
G <- G.left
else if (i,j) in G.right
G <- G.right
else
dict(val) <- null
end do
end if-else
end do
end for
for each val in G(1,1,m,n)
if dict(val) not null
solution.append(val)
end if
end for
return solution
The Java code:
class Grid{
int a, b, c, d;
Grid left, right;
Grid(int a, int b, int c, int d){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
left = right = null;
}
public boolean isInGrid(int e, int f){
return (e >= a && e <= c && f >= b && f <= d);
}
public boolean hasNoChild(){
return (left == null && right == null);
}
}
public static int[] findPath(int[][] arr){
int row = arr.length;
int col = arr[0].length;
int[][] index = new int[row*col+1][2];
HashMap<Integer,Point> map = new HashMap<Integer,Point>();
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
map.put(arr[i][j], new Point(i,j));
}
}
Grid root = new Grid(0,0,row-1,col-1);
SortedSet<Integer> keys = new TreeSet<Integer>(map.keySet());
for(Integer entry : keys){
Grid temp = root;
int x = map.get(entry).x, y = map.get(entry).y;
while(temp.isInGrid(x, y)){
if(temp.hasNoChild()){
temp.left = new Grid(temp.a,temp.b,x, y);
temp.right = new Grid(x, y,temp.c,temp.d);
break;
}
if(temp.left.isInGrid(x, y)){
temp = temp.left;
}
else if(temp.right.isInGrid(x, y)){
temp = temp.right;
}
else{
map.get(entry).x = -1;
break;
}
}
}
int[] solution = new int[row+col-1];
int count = 0;
for(int i = 0 ; i < row; i++){
for(int j = 0; j < col; j++){
if(map.get(arr[i][j]).x >= 0){
solution[count++] = arr[i][j];
}
}
}
return solution;
}
The space complexity is constituted by maintenance of dictionary - O(NM) and of the tree - O(N+M). Overall: O(NM)
The time complexity for filling up and then sorting the dictionary - O(NM log(NM)); for checking the tree for each of the NM values - O(NM log(N+M)). Overall - O(NM log(NM)).
Of course, this won't work if values are repeated since then we'd have more than one (i,j)'s for a single value in the grid and the decision to chose which will no longer be satisfied by a greedy approach.
Additional FYI: The problem similar to this I heard about earlier had an additional grid property - there are no values repeating and the numbers are from 1 to NM. In such a case, the complexity could further reduce to O(NM log(N+M)) since instead of a dictionary, you can simply use values in the grid as indices of an array (which won't required sorting.)

Resources