【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;
}

猜你喜欢

转载自www.cnblogs.com/streamazure/p/12901682.html