国家铁路
题目描述
dls的算竞王国可以被表示为一个有 HH行和 WW列的网格,我们让 (i,j)(i,j)表示从北边第ii行和从西边第jj列的网格。最近此王国的公民希望国王能够修建一条铁路。
铁路的修建分为两个阶段:
- 从所有网格中挑选2个不同的网格,在这两个网格上分别修建一个火车站。在一个网络上修建一个火车站的代价是Ai,jAi,j。
- 在这两个网格间修建一条铁轨,假设我们选择的网格是 (x1,y1)(x1,y1)和(x2,y2)(x2,y2),其代价是 C×(|x1−x2|+|y1−y2|)C×(|x1−x2|+|y1−y2|)。
dls的愿望是希望用最少的花费去修建一条铁路造福公民们。现在请你求出这个最小花费。
题目输入
第一行输入三个整数分别代表H,W,C(2≤H,W≤1000,1≤C≤109)H,W,C(2≤H,W≤1000,1≤C≤109)。
接下来HH行,每行WW个整数,代表Ai,j(1≤Ai,j≤109)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
思路
先去绝对值。那么x2,y2一定是在x1,y1的右下方。
推导得到(x1,y1)到(x2,y2)的代价是Ax1,y1-c*(x1+y1)+Ax2,y2+c*(x2+y2);可以这样想,期望前面一个点的代价**Ax1,y1-c*(x1+y1)**尽量小,这样选择其右下方的点遍历后才有希望达到最小。换个角度,从当前节点选择左上方的一个最小代价点这样在当前点建铁路代价最低。
mi[][]为前缀最小值,保存(1,1)到(x,y)的Ax1,y1-c*(x1+y1)的最小值。然后再次遍历求出最小和即可。
但是还有一种情况,就是第二个点在第一个点的左下方,这时候要维护的前缀最小值发生改变,操作将会复杂。最简单的做法就是翻转矩阵,然后再跑一次。
#include <bits/stdc++.h>
typedef long double ld;
typedef long long ll;
#define pb push_back
#define mk make_pair
#define mt make_tuple
#define eb emplace_back
#define pob pop_back
#define rz resize
#define mem(a,b) memset(a,b,sizeof(a))
#define all(a) (a).begin(),(a).end()
#define rall(a) (a).rbegin(),(a).rend()
#define debug(a) cout<<#a<<"="<<a<<endl;
#define xx first
#define yy second
#define qwe(i,a,b) for(int i=a;i<=b;i++)
#define ewq(i,a,b) for(int i=a;i>=b;i--)
inline ll rr(){ll f=1,x=0;char ch;do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return f*x;}
using namespace std;
const ll INF=0x3f3f3f3f,inf=0x3f3f3f3f3f3f3f;
const ll mod[2]={int(1e9 + 7), 10007};
const int base[2]={29,31};
const int maxn=1e3+6;
ll n,m,c;
ll a[maxn][maxn],mi[maxn][maxn],ans=inf,t[maxn][maxn]; // 原数组 前缀最小数组 答案 临时数组
void get_mi() {
mem(mi,127);
qwe(i,1,n)
qwe(j,1,m) {
mi[i][j]=min({a[i][j]-c*(i+j),mi[i-1][j],mi[i][j-1]});
}
}
void reverse() {
qwe(i,1,n)
qwe(j,1,m) {
t[i][j]=a[i][m-j+1];
}
memcpy(a,t,sizeof t);
}
void solve() {
get_mi();
qwe(i,1,n)
qwe(j,1,m) {
ans=min({a[i][j]+c*(i+j)+mi[i-1][j],a[i][j]+c*(i+j)+mi[i][j-1],ans});
}
}
int main(int argc, char const *argv[]) {
// ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
n=rr(),m=rr(),c=rr();
qwe(i,1,n) qwe(j,1,m) a[i][j]=rr();
solve();
reverse();
solve();
std::cout << ans << '\n';
return 0;
}