/*
    题意:n行m列 n<=100 , m<=5000
              每一个位置有值T[i][j] , F[i][j]
              现要在每一行都找一个点来建站,其需要花费的时间为T[i][j]
              有一个要求:
               if we build a tower in cell (i,j) and another tower in cell (i+1,k), then we shall have |j-k|≤f(i,j)+f(i+1,k). 
   求最少时间
    
    dp[i,j] = min{dp[i+1,k]} +T[i,j]
   O(nm^2)是不行的
   |j-k| ≤ f(i,j)+f(i+1,k) 等价于
   j-f(i,j) <= k + f(i+1,k)  且 j+f(i,j) >= k - f(i+1,k)
   对于行i可先按照j+f(i,j)排序,然后检查每个j,把所有满足j+f(i,j) >= k - f(i+1,k)的k都加进去
   对这些k找出满足 j-f(i,j) <= k + f(i+1,k)的最小的dp[i+1,k]即可
   找最小,用线段树(需要离散化)
   对于一行,每个k只会加进去一次
   总的复杂度是O(nmlogm)
*/

#include
<iostream>
#include
<cstring>
#include
<algorithm>

using namespace std;

const int INF = 1000000000;
const int MAX = 5000;

//#define G(i,x) ((x)-F[i][x])
//#define H(i,x) ((x)+F[i][x])

int T[100][5000],F[100][5000];
int dp[2][5000];
int G[5000] , H[5000] , index1[5000] ,  index2[5000];
int N , M;

bool cmpG(const int &a , const int &b)
{
    
return G[a] < G[b];
}

bool cmpH(const int &a , const int &b)
{
    
return H[a] < H[b];
}


int Min[MAX*4];

void insert(int p , int left , int right , int pos , int val)
{
    
if(Min[p] > val)Min[p] = val;
    
if(left == right)return;
    
int mid = (left+right)>>1;
    
if(pos <= mid)insert(2*p , left , mid , pos , val);
    
else insert(2*p+1 , mid+1 , right , pos , val);
}


int query(int p , int left , int right , int l  , int r)
{
    
if(l <= left && right <= r)return Min[p];
    
int mid = (left+right)>>1;
    
if(r <= mid)return query(2*p , left , mid , l , r);
    
if( l > mid )return query(2*p+1 , mid+1 , right , l , r);
    
return min(query(2*p , left , mid , l , mid) , query(2*p+1, mid+1 , right , mid+1 , r));
}


#define getPos(x) (lower_bound(compress,compress+num,(x))-compress) 

int main()
{
    
while(scanf("%d%d",&N,&M),N||M)    
    
{
        
for(int i = 0 ; i < N ; i++)
            
for(int j = 0 ; j < M ; j ++)
            
{
                scanf(
"%d",&T[i][j]);
            }

        
for(int i = 0 ; i < N ; i++)
            
for(int j = 0 ; j < M ; j ++)
            
{
                scanf(
"%d",&F[i][j]);
            }


        
int pre = 0 , now = 1;
        
for(int j = 0 ; j < M ;  j++)
        
{
            dp[pre][j] 
= T[N-1][j];
        }

        
for(int i = N - 2 ; i >= 0 ; i --)
        
{
            
for(int j = 0 ; j < M ; j ++)
            
{
                H[j] 
= j + F[i][j];
                index1[j] 
= j;
            }

            sort(index1 , index1 
+ M , cmpH);

            
for(int k = 0 ; k < M ; k ++)
            
{
                G[k] 
= k - F[i+1][k];
                index2[k] 
= k;
            }

            sort(index2 , index2 
+ M , cmpG);
            
            
//离散化
            static int compress[5000];
            
for(int k = 0 ; k < M ; k ++)
                compress[k] 
= k + F[i+1][k];
            sort(compress,compress
+M);
            
int num = unique(compress , compress+M) - compress;

            fill(Min,Min
+num*4 , INF);

            
for(int jj = 0 , kk = 0; jj <M ; jj++)
            
{
                
int j = index1[jj] , k;    
                
// j + F[i,j] >= k - F[i+1][k]
                while(kk < M && G[k=index2[kk]] <= H[j] )
                
{
                    kk
++;
                    insert(
1 ,0 , num , getPos(k+F[i+1][k]) , dp[pre][k] );
                }

                
//j-F[i,j] <= k+F[i+1,k]
                dp[now][j] = query(1 , 0 , num , getPos(max(0,j-F[i][j])) , num) + T[i][j];
            }

            swap(pre,now);
        }

        printf(
"%d\n",*min_element(dp[pre],dp[pre]+M));
    }

    
return 0;
}