P3431 [POI2005]AUT-The Bus 树状数组+DP

这篇博客讨论了如何解决一个城市公交线路优化问题,以接载最多乘客。通过建立动态规划模型并利用前缀和与树状数组进行高效求解,实现了在大规模棋盘网格中找到最优路径。代码示例展示了实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目大意:

Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 到 m编号. 每个路口用两个数(i, j) 表示(1 <= i <= n, 1 <= j <= m). Byte City里有一条公交线, 在某一些路口设置了公交站点. 公交车从 (1, 1) 发车, 在(n, m)结束.公交车只能往北或往东走. 现在有一些乘客在某些站点等车. 公交车司机希望在路线中能接到尽量多的乘客.帮他想想怎么才能接到最多的乘客.

n和m范围很大,但是我们先考虑如果n和m不那么大的时侯就是一个很简单的DP。转移方程为 d p [ i ] [ [ j ] = a [ i ] [ j ] + m a x ( d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][[j] =a[i][j]+max(dp[i-1][j]+dp[i][j-1] dp[i][[j]=a[i][j]+max(dp[i1][j]+dp[i][j1]。然后这种方程的其实就是找一个矩阵的最值,如果我们把 d p [ i ] [ j ] dp[i][j] dp[i][j]初始化为0的话,其实这个方程就变成了 d p [ i ] [ j ] = a [ i ] [ j ] + m a x ( d p [ x ] [ y ] ) , 1 < = x < = i , 1 < = y < = j dp[i][j]=a[i][j] +max(dp[x][y]),1<=x<=i,1<=y<=j dp[i][j]=a[i][j]+max(dp[x][y]),1<=x<=i,1<=y<=j。然后我们就可以用前缀和的思想来维护这个矩阵的最值,按照x,y升序排序,y离散化一下,就相当于每次都是维护一行的最值,用树状数组查询和询问,最终复杂度是 k ∗ l o g ( k ) k*log(k) klog(k)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const double Pi = acos(-1);
namespace {
  template <typename T> inline void read(T &x) {
    x = 0; T f = 1;char s = getchar();
    for(; !isdigit(s); s = getchar()) if(s == '-') f = -1;
    for(;  isdigit(s); s = getchar()) x = (x << 3) + (x << 1) + (s ^ 48);
    x *= f;
  }
}
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define _for(n,m,i) for (register int i = (n); i <  (m); ++i)
#define _rep(n,m,i) for (register int i = (n); i <= (m); ++i)
#define _srep(n,m,i)for (register int i = (n); i >= (m); i--)
#define _sfor(n,m,i)for (register int i = (n); i >  (m); i--)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define lowbit(x) x & (-x)
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e5+5;
struct node {
  int x, y, val;
  bool operator < (const node &a) const {
    return x == a.x ? y < a.y : x < a.x;
  }
}e[N];
vector<int> ve;
int getid(int x) {
  return lower_bound(ve.begin(),ve.end(), x) - ve.begin() + 1;
}
int c[N], len;
void upd(int pos, int x) {
  for(; pos <= len; pos += lowbit(pos)) c[pos] = max(x, c[pos]);
}
int qry(int pos) { 
  int ans = 0;
  for(; pos; pos -= lowbit(pos)) ans = max(ans, c[pos]);
  return ans;
} 
int main() { 
  int n, m, k; scanf("%d %d %d", &n, &m, &k);
  _for(0, k, i) scanf("%d %d %d", &e[i].x, &e[i].y, &e[i].val), ve.push_back(e[i].y);
  sort(e, e + k);
  sort(ve.begin(), ve.end());
  ve.erase(unique(ve.begin(), ve.end()), ve.end());
  len = ve.size();
  int ans = 0, res;
  _for(0, k, i) { 
    e[i].y = getid(e[i].y);
    res = qry(e[i].y) + e[i].val;
    ans = max(ans ,res); 
    upd(e[i].y, res);
  } 
  cout << ans << endl;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值