题目
题意:
给定一个二维数组,每个位置有高度。一开始在(1,1)点,要走到(n,m)点。每次往下走或往右走,但是必须满足下一个位置的高度比当前位置恰好大1。可以进行操作让某一个点的高度减1,要求输出从左上到右下需要操作的次数最少。
1
≤
n
,
m
≤
100
,
1
≤
a
i
,
j
≤
1
0
15
1≤n,m≤100,1≤a_{i,j}≤10^{15}
1≤n,m≤100,1≤ai,j≤1015
分析:
其实认真分析后可以发现,在最后答案的路径中,一定是有一个点没有被变化的。因为如果路径上所有点都被减1了,那我可以都不用减1就可以满足。既然一定有一个点没有变化,那么直接枚举不动点,那么路径上的其他点就随之确定了。确定之后dp转移代价即可。
#include <iostream>
using namespace std;
typedef long long ll;
ll a[105][105],v[105][105],dp[105][105];
ll slove(int x,int y,int n,int m)
{
ll t = a[x][y] - (x+y-2);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
v[i][j] = a[i][j] - (t + i + j - 2);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
dp[i][j] = -1;
if( v[i][j] < 0 ) continue;
if( i == 1 && j == 1 ) dp[i][j] = v[i][j];
else if( i == 1 )
{
ll t = dp[i][j-1];
if( t != -1 ) dp[i][j] = t + v[i][j];
}else if( j == 1 )
{
ll t = dp[i-1][j];
if( t != -1 ) dp[i][j] = t + v[i][j];
}else
{
ll t1 = dp[i-1][j],t2 = dp[i][j-1];
if( t1 != -1 ) dp[i][j] = t1 + v[i][j];
if( t2 != -1 )
{
if( dp[i][j] == -1 ) dp[i][j] = t2 + v[i][j];
else dp[i][j] = min(dp[i][j],t2+v[i][j]);
}
}
}
}
if( dp[n][m] != -1 ) return dp[n][m];
else return 1e18;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while( t-- )
{
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> a[i][j];
}
}
ll ans = 1e18;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
ans = min(ans,slove(i,j,n,m));
}
}
cout << ans << '\n';
}
return 0;
}