ABC 210 D - National Railway

D - National Railway

矩阵DP,拆绝对值

设向下为 \(x\) 正方向,向右为 \(y\) 正方向

设两个点为 \((x_1,y_1),\;(x_2,y_2)\), 代价为 \(A_{x_1y_1}+A_{x_2y_2}+|x_1-x_2|+|y_1-y_2|\)

想办法拆掉绝对值

  1. \((x_2,y_2)\)\((x_1,y_1)\) 右下方,则 \(|x_1-x_2|+|y_1-y_2|=(x_2+y_2)-(x_1+y_1)\)

    \(cost=A_{x_2y_2}+(x_2+y_2)+(A_{x_1y_1}-(x_1+y_1))\)

  2. \((x_2,y_2)\)\((x_1,y_1)\) 左下方,则 \(|x_1-x_2|+|y_1-y_2|=(x_2-y_2)-(x_1-y_1)\)

    \(cost=A_{x_2y_2}+(x_2-y_2)+(A_{x_1y_1}-(x_1-y_1))\)

在第一种方案中,设 \(f[i][j]\)\((i,j)\) 为右下角的小矩阵中,\(A_{xy}-(x+y)\) 的最小值

\(f[i][j]=min({f[i-1][j],f[i][j-1],A_{ij}-(i+j)})\)

所以枚举 \((i,j)\) 作为 \((x_2,y_2)\), \((x_1,y_1)\) 的最小代价就是 \(f[i][j]\)(注意先更新 \(f[i][j]=min(f[i-1][j],f[i][j-1])\) 后求出当前的答案,再更新 \(f[i][j]=min(f[i][j],A_{ij}-(i+j))\), 因为 \((x_1,y_1)\neq(x_2,y_2)\)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 1e3 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m;
ll c, a[N][N];
ll f[N][N];

ll solve1()
{
	ll ans = INF;
	memset(f, 0x3f, sizeof f);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			f[i][j] = min(f[i-1][j], f[i][j-1]);
			ans = min(ans, a[i][j] + c * (i + j) + f[i][j]);
			f[i][j] = min(f[i][j], a[i][j] - c * (i + j));
		}
	}
	return ans;
}

ll solve2()
{
	ll ans = INF;
	memset(f, 0x3f, sizeof f);
	for (int i = 1; i <= n; i++)
	{
		for (int j = m; j >= 1; j--)
		{
			f[i][j] = min(f[i-1][j], f[i][j+1]);
			ans = min(ans, a[i][j] + c * (i - j) + f[i][j]);
			f[i][j] = min(f[i][j], a[i][j] - c * (i - j));
		}
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> m >> c;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			cin >> a[i][j];
		
	ll ans = min(solve1(), solve2());
	cout << ans << endl;
    return 0;
}
posted @ 2022-06-09 20:10  hzy0227  阅读(24)  评论(0编辑  收藏  举报