题意翻译
给出一个 的矩阵,每个矩阵的权值代表该点的初始高度。
现在需要从点 走到点 ,每一步需要满足以下条件:
- 只能向右或向下
- 设当前格子的高度为 ,只能移动到高度为 的格子上去
初始时可以进行操作,使得某个格子的高度减少一个单位。
问最少需要进行多少次操作,可以存在至少一条从点 到点 的路线
输入格式
第一行包含一个整数,表示数据组数
其中,每一组数据的第一行包含两个整数,表示矩阵的行数与列数
之后是一个的矩阵,表示初始状态下矩阵中每个点的高度
输出格式
对于每组数据,输出一行一个整数,表示最少的操作次数
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
数据范围和约定
对于 的数据,保证 , ,
分析
很套路的题,但也有一定难度,放在的题完全可以接受
读完题后,很容易想到此题的一个弱化版本:给定一个矩阵,每次只可以向下或向右移动,如何行走才能使得经过点的点权之和最大?
思考如何把这个题转化过去。不难发现,如果知道了这个点的起点高度,则走到坐标为的点上高度应该为。
所以就有了以下两点推论:
设一个点坐标为,若,则该点不可能被经过。
若,则经过该点所需要的操作次数为。
也就是说,我们只需要把的点的点权设为即可。
然后考虑如何解决上面的弱化版问题。
设表示从走到所需要的最少次数,则。
之后考虑如何转移。首先,对于点,有
之后考虑向外拓展。若,则
也同理,若,则
最后在外面套一层的枚举,枚举矩阵元素并倒推初始点的权值,即。
每一次的时间复杂度为,加上外层的枚举,时间复杂度为。足以跑过的数据。
剩下的见代码即可。
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; }