/*动态规划 矩阵大小MxN
遍历了,时间复杂度O(MxN)
dp大小int [M][N],空间复杂度O(MxN)
* */
public static int minPathSum1(int[][]m){
if(m==null||m.length==0||m[0].length==0||m[0]==null){
return 0;
}
int row=m.length; //行
int col=m[0].length; //列
int[][]dp=new int[row][col];
//起点
dp[0][0]=m[0][0];
//第一行
for (int i = 1; i <col ; i++) {
dp[0][i]=dp[0][i-1]+m[0][i];
}
//第一列
for (int i = 1; i <row ; i++) {
dp[i][0]=dp[i-1][0]+m[i][0];
}
for (int i = 1; i <row ; i++) {
for (int j = 1; j <col ; j++) {
dp[i][j]=Math.min(dp[i-1][j], dp[i][j-1])+m[i][j];
}
}
return dp[row-1][col-1];
}
//时间复杂度不变,空间复杂度O(min{M,N})
public static int minPathSum2(int[][]m) {
if (m == null || m.length == 0 || m[0].length == 0 || m[0] == null) {
return 0;
}
int more=Math.max(m.length,m[0].length); //行数与列数中较大的
int less=Math.min(m.length,m[0].length); //行数与列数中较小的
boolean rowmore= m.length==more; //true:行数较大
int[]arr=new int[less];
arr[0]=m[0][0];
for (int i = 1; i < less; i++) {
arr[i]=arr[i-1]+(rowmore?m[0][i]:m[i][0]);
}
for (int i = 1; i <more ; i++) {
//换行更新
arr[0]=arr[0]+(rowmore?m[i][0]:m[0][i]);
for (int j = 1; j <less ; j++) {
arr[j]= Math.min(arr[j-1],arr[j]) + (rowmore?m[i][j]:m[j][i]);
}
}
return arr[less-1];
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] arr = new int[n][m];
for(int i=0;i<n;i++){
for(int j = 0;j<m;j++){
arr[i][j] = sc.nextInt();
}
}
System.out.println(minPathSum1(arr));
}