国家铁路(二维前缀最小值)

国家铁路

题目描述

dls的算竞王国可以被表示为一个有 HH行和 WW列的网格,我们让 (i,j)(i,j)表示从北边第ii行和从西边第jj列的网格。最近此王国的公民希望国王能够修建一条铁路。

铁路的修建分为两个阶段:

  1. 从所有网格中挑选2个不同的网格,在这两个网格上分别修建一个火车站。在一个网络上修建一个火车站的代价是Ai,jAi,j。
  2. 在这两个网格间修建一条铁轨,假设我们选择的网格是 (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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值