题目描述
d l s dls dls的算竞王国可以被表示为一个有 H H H行和 W W W列的网格,我们让 ( i , j ) (i,j) (i,j)表示从北边第 i i i行和从西边第 j j j列的网格。最近此王国的公民希望国王能够修建一条铁路。
铁路的修建分为两个阶段:
- 从所有网格中挑选 2 2 2个不同的网格,在这两个网格上分别修建一个火车站。在一个网络上修建一个火车站的代价是 A i , j A_{i,j} Ai,j。
- 在这两个网格间修建一条铁轨,假设我们选择的网格是 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2),其代价是 C × ( ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ ) C×(|x_1−x_2|+|y_1−y_2|) C×(∣x1−x2∣+∣y1−y2∣)。
d l s dls dls的愿望是希望用最少的花费去修建一条铁路造福公民们。现在请你求出这个最小花费。
题目输入
第一行输入三个整数分别代表 H , W , C ( 2 ≤ H , W ≤ 1000 , 1 ≤ C ≤ 1 0 9 ) H,W,C(2≤H,W≤1000,1≤C≤10^9) H,W,C(2≤H,W≤1000,1≤C≤109)。
接下来 H H H行,每行 W W W个整数,代表 A i , j ( 1 ≤ A i , j ≤ 1 0 9 ) A_{i,j}(1≤A_{i,j}≤10^9) Ai,j(1≤Ai,j≤109)。
题目输出
输出一个整数代表最小花费。
样例输入1
3 4 2
1 7 7 9
9 6 3 7
7 8 6 4
样例输出1
10
样例输入2
3 3 1000000000
1000000 1000000 1
1000000 1000000 1000000
1 1000000 1000000
样例输出2
1001000001
解题思路
这道题有亿点点难QAQ。
直接枚举的时间复杂度为 o ( n 4 ) o(n^4) o(n4),直接 T T T飞,所以我们需要想办法优化。
题中给出,修建一条铁轨的总花费为 A x 1 , y 1 + A x 2 , y 2 + C ∗ ( ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ ) A_{x_1,y_1}+A_{x_2,y_2}+C*(|x_1-x_2|+|y_1-y_2|) Ax1,y1+Ax2,y2+C∗(∣x1−x2∣+∣y1−y2∣)。
显然,交换一下 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的坐标对花费没有任何影响。
所以,为了方便计算,我们规定情况 1 1 1为 x 1 ≥ x 2 , y 1 ≥ y 2 x_1\ge x_2,y_1\ge y_2 x1≥x2,y1≥y2,情况 2 2 2为 x 1 ≥ x 2 , y 1 ≤ y 2 x_1\ge x_2,y_1\le y_2 x1≥x2,y1≤y2。
直观的来说,以上两种情况分别为矩阵的主对角线方向和副对角线方向。
只需要讨论一种情况的算法,另外一种情况则不证自明:
对于情况
1
1
1,总花费可以写为:
c
o
s
t
_
s
u
m
=
A
x
1
,
y
1
+
A
x
2
,
y
2
+
C
∗
(
x
1
−
x
2
+
y
1
−
y
2
)
=
A
x
1
,
y
1
+
C
∗
(
x
1
+
y
1
)
+
A
x
2
,
y
2
−
C
∗
(
x
2
+
y
2
)
\begin{aligned} cost\_sum & = A_{x_1,y_1}+A_{x_2,y_2}+C*(x_1-x_2+y_1-y_2) \\ & = A_{x_1,y_1}+C*(x_1+y_1)+A_{x_2,y_2}-C*(x_2+y_2) \end{aligned}
cost_sum=Ax1,y1+Ax2,y2+C∗(x1−x2+y1−y2)=Ax1,y1+C∗(x1+y1)+Ax2,y2−C∗(x2+y2)
在公式的形式简化之后,再去考虑寻找最小值的问题:
我们发现对于给定的 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),我们需要枚举所有符合条件的 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)(条件为 x 1 ≥ x 2 , y 1 ≥ y 2 x_1\ge x_2,y_1\ge y_2 x1≥x2,y1≥y2,在一个小矩阵中),并找出最小值。
但与之前不同,我们现在可以对 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)进行动态规划,然后就可以在 O ( 1 ) O(1) O(1)时间内获取对于给定 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)的最小值。
规划公式为dp[i][j] = min{ dp[i][j-1], dp[i-1][j], station[i][j] - C * (i + j)}
。
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
dp[i][j] = min(dp[i][j - 1], min(dp[i - 1][j], station[i][j] - C * (i + j)));
}
}
那么计算主对角线情况下的最小值,只需要枚举另外一个点:
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
ans = min(ans, min(dp[i - 1][j] + dp[i][j - 1]) + station[i][j] + C * (i + j));
}
}
最后简单给出计算副对角线情况下最小值的代码以及AC代码:
副对角线算法:
for (int i = 1; i <= h; i++) {
for (int j = w; j >= 1; j--) {
dp[i][j] = min(dp[i][j + 1], min(dp[i - 1][j], station[i][j] + C * (j - i)));
}
}
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
ans = min(ans, min(dp[i - 1][j], dp[i][j + 1]) + station[i][j] + C * (i - j));
}
}
AC代码:
#include <iostream>
#include <string.h>
using namespace std;
const long long max_h = 1000;
const long long max_w = 1000;
const long long NaN = 0x3F3F3F3F3F3F3F3F;
const long long max_c = 1e9;
long long station[max_h + 2][max_w + 2], dp1[max_h + 2][max_w + 2], dp2[max_h + 2][max_w + 2]; //多开一圈,防止越界
long long h, w, C;
int main() {
cin >> h >> w >> C;
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
cin >> station[i][j];
}
}
//DP1(主对角线)
memset(dp1, 0x3F, sizeof(long long) * (max_h + 2) * (max_w + 2));
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
dp1[i][j] = min(dp1[i - 1][j], min(dp1[i][j - 1], station[i][j] - C * (i + j)));
}
}
//DP2(副对角线)
memset(dp2, 0x3F, sizeof(long long) * (max_h + 2) * (max_w + 2));
for (int i = 1; i <= h; i++) {
for (int j = w; j >= 1; j--) {
dp2[i][j] = min(dp2[i][j + 1], min(dp2[i - 1][j], station[i][j] + C * (j - i)));
}
}
long long ans = NaN;
//ans1(主对角线)
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
ans = min(ans, min(dp1[i - 1][j], dp1[i][j - 1]) + station[i][j] + C * (i + j));
}
}
//ans2(副对角线)
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
ans = min(ans, min(dp2[i - 1][j], dp2[i][j + 1]) + station[i][j] + C * (i - j));
}
}
cout << ans << endl;
return 0;
}