There are n × n cells on a grid, the top-left cell is at (1, 1) while the bottom-right cell is at (n, n). You start at (1, 1) and move to (n, n). At any cell (i, j), you can move to (i + 1, j) or (i, j + 1), provided that you don’t move out of the grid. Clearly, you will make exactly 2n − 2 steps. When you are at cell (i, j), including the starting point (1, 1) and the destination (n, n), you can take all the ai,j diamonds at this cell, and have a chance to raise the price of each diamond by bi,j dollars. You will sell all the diamonds you have with the final price in the end, and your goal is to choose the optimal path that will maximize your profits. Note that initially the price of each diamond is zero, and you have nothing to sell.
Input
The first line contains a single integer T (1 ≤ T ≤ 10), the number of test cases. For each test case: The first line contains a single integer n (1 ≤ n ≤ 100), denoting the size of the grid. Each of the following n lines contains n integers, the i-th line contains ai,1, ai,2, . . . , ai,n (1 ≤ ai,j ≤ 106 ), denoting the number of diamonds in each cell. Each of the following n lines contains n integers, the i-th line contains bi,1, bi,2, . . . , bi,n (1 ≤ bi,j ≤ 106 ), denoting how much you can raise the price in each cell. It is guaranteed that all the values of ai,j and bi,j are chosen uniformly at random from integers in [1, 106 ]. The randomness condition does not apply to the sample test case, but your solution must pass the sample as well.
Output
For each test case, output a single line containing an integer: the maximum number of dollars you can earn by selling diamonds.
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
typedef vector<P>V;
const int N=105;
int Case,n,m,i,j,k,a[N][N],b[N][N];ll ans;V f[N][N];P pool[1000005];
inline void ext(const P&t){
while(m&&pool[m].second<=t.second)m--;
if(!m||pool[m].first<t.first)pool[++m]=t;
}
inline void merge(const V&A,const V&B,V&C){
int ca=A.size(),cb=B.size(),i=0,j=0;
m=0;
while(i<ca&&j<cb)ext(A[i].first<B[j].first?A[i++]:B[j++]);
while(i<ca)ext(A[i++]);
while(j<cb)ext(B[j++]);
C.resize(m);
for(i=0;i<m;i++)C[i]=pool[i+1];
}
int main(){
scanf("%d",&Case);
while(Case--){
scanf("%d",&n);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&a[i][j]);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)scanf("%d",&b[i][j]);
f[1][1].resize(1);
f[1][1][0]=P(a[1][1],b[1][1]);
for(i=1;i<=n;i++)for(j=1;j<=n;j++){
if(i==1&&j==1)continue;
if(i==1)f[i][j]=f[i][j-1];
else if(j==1)f[i][j]=f[i-1][j];
else merge(f[i-1][j],f[i][j-1],f[i][j]);
for(k=0;k<f[i][j].size();k++){
f[i][j][k].first+=a[i][j];
f[i][j][k].second+=b[i][j];
}
}
ans=0;
for(i=0;i<f[n][n].size();i++)ans=max(ans,1LL*f[n][n][i].first*f[n][n][i].second);
printf("%lld\n",ans);
}
}