zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    【PHP内存泄漏案例】PHP对象递归引用造成内存泄漏
    【总结】/etc/rc.d/rc.local 与 /etc/profile .bash_profile .bashrc 文件执行顺序
    MySQL数据类型
    PHP通用分页(Pager)类
    【抚琴煮酒】我们的网站压力究竟在哪里?
    Linux/CentOS 服务安装/卸载,开机启动chkconfig命令详解|如何让MySQL、Apache开机启动?
    /etc/rc.d/rc与/etc/rc.d/init.d的关系
    PHP正则表达式30分钟入门教程
    数学之路-分布式计算-disco(4)
    数据库中存储日期的字段类型究竟应该用varchar还是datetime ?
  • 原文地址:https://www.cnblogs.com/streamazure/p/12901682.html
Copyright © 2011-2022 走看看