题意翻译

给出一个 的矩阵,每个矩阵的权值代表该点的初始高度。

现在需要从点 走到点 ,每一步需要满足以下条件:

  • 只能向右或向下
  • 设当前格子的高度为 ,只能移动到高度为 的格子上去

初始时可以进行操作,使得某个格子的高度减少一个单位。

问最少需要进行多少次操作,可以存在至少一条从点 到点 的路线


输入格式

第一行包含一个整数,表示数据组数

其中,每一组数据的第一行包含两个整数,表示矩阵的行数与列数

之后是一个的矩阵,表示初始状态下矩阵中每个点的高度


输出格式

对于每组数据,输出一行一个整数,表示最少的操作次数


Input & Output 's examples

Input 's eg

5
3 4
1 2 3 4
5 6 7 8
9 10 11 12
5 5
2 5 4 8 3
9 10 11 5 1
12 8 4 2 5
2 2 5 4 1
6 8 2 4 2
2 2
100 10
10 1
1 2
123456789876543 987654321234567
1 1
42

Output 's eg

9
49
111
864197531358023
0

数据范围和约定

对于 的数据,保证 , ,


分析

很套路的题,但也有一定难度,放在题完全可以接受

读完题后,很容易想到此题的一个弱化版本:给定一个矩阵,每次只可以向下或向右移动,如何行走才能使得经过点的点权之和最大

思考如何把这个题转化过去。不难发现,如果知道了这个点的起点高度,则走到坐标为的点上高度应该为

所以就有了以下两点推论:

  1. 设一个点坐标为,若,则该点不可能被经过。

  2. ,则经过该点所需要的操作次数为

也就是说,我们只需要把的点的点权设为即可。

然后考虑如何解决上面的弱化版问题。

表示从走到所需要的最少次数,则

之后考虑如何转移。首先,对于点,有

之后考虑向外拓展。若,则

也同理,若,则

最后在外面套一层的枚举,枚举矩阵元素并倒推初始点的权值,即

每一次的时间复杂度为,加上外层的枚举,时间复杂度为。足以跑过的数据。

剩下的见代码即可。


Code[Accepted]

#include <algorithm>
#include <iostream>
#include <cstring>
#include <iomanip>
#include <cstdlib>
#include <sstream>
#include <cstdio>
#include <string>
#include <vector>
#include <cctype>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <map>
#include <set>

#define I inline
#define ll long long
#define pb push_back
#define MP make_pair
#define ull unsigned long long
#define PII pair<int , int>
#define PIL pair<int , long long>
#define PSL pair<string , long long>
#define PLL pair<long long , long long>
#define all(x) (x).begin() , (x).end()
#define copy(a , b) memcpy(a , b , sizeof(b))
#define clean(a , b) memset(a , b , sizeof(a))
#define rep(i , l , r) for (int i = (l); i <= (r); i ++)
#define per(i , r , l) for (int i = (r); i >= (l); i --)
#define PE(i , x) for(int i = head[x]; i; i = edge[i].last)
#define DEBUG(x) std :: cerr << #x << '=' << x << std :: endl

using namespace std;

const int N = 10001;
const int M = 100001;
const int HA = 998244353;
const int INF = 2147483647;
const long long INFL = 9023372036854775801;

ll a[101][101] , f[101][101];

ll solve(ll n , ll m , ll sta){
    rep(i , 1 , n){
        rep(j , 1 , m){
            f[i][j] = INFL;
        }
    }
    f[1][1] = 0;
    rep(i , 1 , n){
        rep(j , 1 , m){
            ll val = i + j + sta;
            if(val > a[i][j]){
                f[i][j] = INFL;
                continue;
            }
            f[i][j] += a[i][j] - val;
            if(i + 1 <= n){
                f[i + 1][j] = min(f[i + 1][j] , f[i][j]);
            }
            if(j + 1 <= m){
                f[i][j + 1] = min(f[i][j + 1] , f[i][j]);
            }
        }
    }
    return f[n][m];
}

int main() {
    #ifdef LOCAL
        freopen("try.in" , "r" , stdin);
        freopen("try1.out" , "w" , stdout);
    #endif
    ll t , n , m;
    scanf("%lld" , &t);
    while(t --){
        clean(a , 0);
        scanf("%lld%lld" , &n , &m);
        rep(i , 1 , n){
            rep(j , 1 , m){
                scanf("%lld" , &a[i][j]);
            }
        }
        ll ans = INFL;
        rep(i , 1 , n){
            rep(j , 1 , m){
                ans = min(ans , solve(n , m , a[i][j] - i - j));
            }
        }
        printf("%lld\n" , ans);
    }


    return 0;
}

THE END