Notation:
Let MCV (H1, H2, H3, …, Hk) denote the maximum coin value that we can obtain from the given k stacks coins. Hi means the height of the i-th stack. Further, let S (H1, H2, H3, …, Hk) denote the sum of the values for all coins in the k stacks.
Recursive Formulation:
We observe that once the first player pockets one of the top coins in these k stacks, the opponent realizes the maximum value from the remaining coins. Therefore, the value realized by the first player is the sum of all k stacks minus the value realized by the opponent. Therefore, we can write MCV (H1, H2, H3, …, Hk) as:
∀ Hi > 0 {1≤ i ≤k}
MCV (H1, H2, H3, …, Hk) = max {S (H1, H2, H3, …, Hk) - MCV (H1-1, H2, H3, …, Hk), MCV (H1, H2-1, H3, …, Hk), MCV (H1-1, H2, H3-1, …, Hk),…, MCV (H1-1, H2, H3, …, Hk-1) }
This can further be written as:
MCV (H1, H2, H3, …, Hk) = S (H1, H2, H3, …, Hk) – min {MCV (H1-1, H2, H3, …, Hk), MCV (H1, H2-1, H3, …, Hk), MCV (H1, H2, H3-1, …, Hk),…, MCV (H1, H2, H3, …, Hk-1) }
The base case of the recurrence can be set as MCV (0, 0, …,0) = 0, S (0, 0, …,0) = 0.
Algorithm:
The challenge is that we cannot build a k dimensions array like MCV [H1] [ H2] [ H3] …[Hk]. So we transform the k dimensions to one dimension.
//Initailize
int kn=(int) (Math.pow((n+1),k));
int MCV[]=new int[kn];
int S[]=new int[kn];
//Base case
MCV[0]=0;
S[0]=0;
for (int i=1;i<kn;i++){
int[] h=new int[k];//h[] are the height of k stacks
for (int j=0;j<k;j++){
int division=(int)Math.pow((n+1),j);
int mod=i/division;
h[j]=mod%(n+1);
}
for(int j=0;j<k;j++){
if(h[j]>0){
S[i]=S[i-(int)Math.pow((n+1),j)]+v[j][h[j]];
break;}
}
int min=Integer.MAX_VALUE;
for(int j=0;j<k;j++){
if(h[j]>0){
if(MCV[i-(int)Math.pow((n+1),j)]<min){
min=MCV[i-(int)Math.pow((n+1),j)];
}
}
}
MCV[i]=S[i]-min;
}
Time Complexity:
O(n) is kn*k, kn is Math.pow((n+1),k). So, O(n) = k*nk