【DP】F. Decreasing Heights

F. Decreasing Heights

题意:给定一个(n*m)的地图,每个格子有初始高度。只能向右或向下移动,且要求第(i+1)步的高度必须比第(i)步恰好高(1)。每次操作可以使得任意一个格子的高度减(1)。问最少需要几次操作,使得地图中存在由((1,1))((n,m))的路径。

思路:

DP。

由贪心原理可以知道,满足题意的路径中一定经过一个不需要降低高度的格子。以这个格子的高度为基准,计算可能经过的格子的花销,更新dp数组。

(dp[i][j])表示从((1,1))((i,j))所需要的最小操作数

转移方程:

(dp[i][j]=min(dp[i-1][j]+cost,dp[i][j-1]+cost))

而这个花销可以这样计算:

因为只能向右或向下移动,说明相邻两步移动的坐标差恰为1,又高度差也恰为1,所以:设这个基准高度为(H(x,y)),由题可知(H(x,y))与任意格子的应有高度(H(i,j))存在以下关系:

(H(i,j)=H(x,y)-(x-i+y-j))

所以做法是先通过遍历选定基准格子((x,y)),再计算得出可能路径上的格子的应有高度,据此再计算花销。

也可以通过(H(x,y))计算(H(1,1)),再以(H(1,1))计算各格子的应有高度,此时式子可简化为:

(H(i,j)=H(1,1)+i+j-2)

代码采用第二种方法。

typedef long long LL;
const LL INF = 1e18;
const int maxn = 100 + 10;
LL Map[maxn][maxn], dp[maxn][maxn];
int n, m;
LL solve(LL begin) {
    //对于每一个不同的起点值,都要初始化dp数组
    //注意从0开始初始化,否则更新dp[2][1]与dp[1][2]时会出错
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			dp[i][j] = INF;
		}
	}
	dp[1][1] = Map[1][1] - begin;
    //起点格子的花销
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (i == 1 && j == 1) continue;
            //dp[1][1]算过了,跳过
			LL nowh = begin + i + j - 2;
			//以标准高度为基准计算(i,j)格的应有高度
			if (Map[i][j] < nowh) continue;
			//实际高度比应有高度还小,没救了
			LL cost = Map[i][j] - nowh;
			//计算(i,j)格的花费
			dp[i][j] = min(dp[i - 1][j] + cost, dp[i][j - 1] + cost);
		}
	}
	return dp[n][m];
}

int main()
{
	int t; cin >> t; while (t--) {
		LL ans = INF;
		cin >> n >> m;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cin >> Map[i][j];
			}
		}
        
		//尝试通过遍历所有可能的标准高度后计算得出起点值,再从这个起点值开始dp到终点
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				LL begin = Map[i][j] - i - j + 2;
				if(begin<=Map[1][1]) ans = min(ans, solve(begin));
                //否则实际起点值比理论起点值还小,这个起点值是不可用的
			}
		}
		cout << ans << endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/streamazure/p/12901682.html