Recursive 3d dP , With 2d->1d mapping reduction using hashing
I kept 3 variables for a state ,\nIndex_of_pile --> which pile am i currently standing at  --> [i]\nindex_in_current_pile  --> which index is at the top at the 
I kept 3 variables for a state ,\nIndex_of_pile --> which pile am i currently standing at  --> [i]\nindex_in_current_pile  --> which index is at the top at the moment for piles[index], -->  [j]\nNo_of_purchaces_remaining  ---> how many transactions do I have left with me ---> [k]\n\nDp[i][j][k] = returns me the maximum money i can retreive from here \nat this state i have 2 choices \ntake the jth coin (topmost coin) on pile i  and stay on the same pile\ndont take the jth coin and move the next pile\n\ntake : piles[i][j] + func(i , j + 1 , k - 1)\ndont take : func(i + 1 , 0 , k - 1 ) --> 0 because now 0 is the topmost coin of the new pile\n\nTime Complexity : O(piles.size()*K + calculationforhas = Piles.size())\n\n```class Solution {\n   \n    \n   \n    \n    int dp[3003][2001];\n\t\n\t /*\n    state which i can think of is that \n    given (index_of_pile , index_in_pile , no_of_transactions_left) \n    what is the maximum money i can generate \n    index_in_pile---> the index of the coin to be pick from pile(index_of_pile)\n    */\n     \npublic:\n    int maxValueOfCoins(vector<vector<int>>& piles, int k) {\n        memset(dp , -1 , sizeof(dp));\n        vector<vector<int>> has;\n        int cnt = -1;\n        \n        for(int i = 0 ; i < piles.size() ; i ++ ) {\n            vector<int> row;\n            for(int j = 0 ; j < piles[i].size() ; j ++) {\n                row.push_back(-1);\n            }\n            row.push_back(-1);\n            has.push_back(row);\n        }\n        \n       \n        for(int i = 0 ; i < has.size() ; i ++ ) {\n            for(int j = 0 ; j < has[i].size() ; j ++ ) {\n                has[i][j] = ++cnt;\n                // this has function is \n                // converting the row and the col of the  cell \n                // into a single value\n                // has{i , j} ---> cnt\n            }\n        } \n        \n    \n        return func(piles , k , 0 , 0 , has);\n      \n    }\n    \n   \n    \n    int func(vector<vector<int>>& piles, int no_of_transactions , int index , int pileposition , \n         vector<vector<int>>& has  ) {\n        if(index >= piles.size() or no_of_transactions <= 0 ) \n            return 0;\n        \n        // if(pileposition >= piles[index].size()) {\n        //     return 0;\n        // }\n        int i = index;\n        int j = pileposition;\n        int k = no_of_transactions;\n        \n        if(dp[has[i][j]][k] != -1)\n        return dp[has[i][j]][k];    \n    \n        \n        int take = 0;\n        int dont_take = 0;\n        // two choices , i decide to take the index\n        if(pileposition < piles[index].size()) {\n            take = +piles[index][pileposition] + func(piles , no_of_transactions - 1 , index , pileposition + 1 , has);\n        }\n        \n        // I stop taking values from pile number index and start taking from the next pile , hence the pile no index will never be \n        // considered in the future if i make this move\n        // pile position will now become 0 , because am again going to take coins from the top of the new pile \n        // we increment the index of the pile to index + 1\n        dont_take = func(piles , no_of_transactions  , index + 1 , 0 , has);\n        \n        // if(pileposition >= piles[index].size()) {\n        //     // i did this just to make the 3rd dp state call pass , \n        //     // since we dont have entry for pileposition >= piles[index].size\n        //     // in the has table so we have to query this way \n        //     // but am proud of the state table that i came up with myself \n        //     return dont_take;\n        // }\n        return dp[has[i][j]][k] = max(take , dont_take);\n    }\n};
[]