題目大意:給出一個 n * m 的矩陣,每個矩陣的權值代表初始高度,現在需要從點 ( 0 , 0 ) 走到點 ( n - 1? , m - 1 ) ,需要滿足以下條件:
每次只能向右或向下移動
假設當前格子的高度為 x ,只能移動到高度為 x + 1 的格子上去
初始時可以進行操作,使得某個格子的高度減少一個單位,問最少需要進行多少次操作,可以存在至少一條從點 ( 0 , 0 ) 到點 ( n , m ) 的路線
題目分析:因為每次只能向下或向右移動,不難聯想到最基礎的一個 dp 題目,就是給出一個 n * m 的二維矩陣,每個點都有權值,問如何行走能使得從點 ( 0 , 0?) 到點 ( n - 1?, m?- 1 ) 的權值和最大,類似的,在這個題目中如果知道了起點 ( 0?, 0?) 的高度 h ,那么到達任意一個點 ( i , j ) 的高度應該為 h + i + j 才行,所以不難看出:
如果 h + i + j >?a[ i ][ j ] ,說明點 ( i , j ) 不能走,視為障礙物即可
如果 h + i + j <= a[ i ][ j ] ,則經過點 ( i , j ) 的權值為 a[ i ][ j ] - ( h + i + j )
到此為止就和那個基礎的 dp 一模一樣了,時間復雜度為 O( n * n )
但是回到這個題目,初值該如何確定呢?如果暴力枚舉的話,是有 1e15 個值,肯定會 TLE 的,其實不難猜到,選擇初值的最優解肯定是讓初始矩陣中盡可能多的數不變,起碼至少得有一個數不變才行吧,所以我們可以 O( n * n ) 去枚舉:令點 ( i , j ) 這個位置不變的初值,然后維護最小值就可以了
時間復雜度為 O( n ^ 4 )
代碼: ?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;typedef unsigned long long ull;const LL inf=0x3f3f3f3f3f3f3f3f;const int N=110;int n,m;LL maze[N][N],dp[N][N];LL solve(LL st)
{for(int i=0;i<n;i++)for(int j=0;j<m;j++)dp[i][j]=inf;dp[0][0]=0;for(int i=0;i<n;i++)for(int j=0;j<m;j++){LL cost=st+i+j;if(cost>maze[i][j])//如果當前格子不能走,則賦值為inf且不進行狀態轉移{dp[i][j]=inf;continue;}dp[i][j]+=maze[i][j]-cost;if(i+1<n)dp[i+1][j]=min(dp[i+1][j],dp[i][j]);if(j+1<m)dp[i][j+1]=min(dp[i][j+1],dp[i][j]);}return dp[n-1][m-1];
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;while(w--){scanf("%d%d",&n,&m);for(int i=0;i<n;i++)for(int j=0;j<m;j++)scanf("%lld",&maze[i][j]);LL ans=inf;for(int i=0;i<n;i++)for(int j=0;j<m;j++)ans=min(ans,solve(maze[i][j]-i-j));printf("%lld\n",ans);}return 0;
}