HDU-3689 Let the light guide us 线段树+DP

题意:输入n,m,然后两个n*m的矩阵,第一个代表在这个位置建塔的成本,第二个代表在相应位置建塔能照的距离,要求每行建一个,并且相邻两行的两个塔要满足照的距离之和大于等于列号差。求最小成本。

分析:每一行中建立一个塔后会在这行照亮一段距离,如果下一行中的一个点能照到这段区间,表示这两个是可以同时建的。也就是下一行中的一个点要在这一行中的对应区间找一个成本最小值,动态规划的状态传递比较好写,只是一行最多有5000个点,照最小值要用线段树优化。

这个题在场上看出来是要用线段树了,本来线段树就比较弱,又很久没有写过了,结果一个半小时都没有写出来,最终补题的时候也是错了好多遍才出,一直TLE,结果最后是因为数组开小了……无语。。。不过最终能手写出来还是挺有成就感的。

#include <string.h>
#include <stdio.h>
int INF = 0x3f3f3f3f;
struct node {
    int b, e, d, same;
}id[2][22000];
int f[105][5005];
int t[105][5005];
void init(int f, int u, int b, int e) {
    id[f][u].b = b;
    id[f][u].e = e;
    id[f][u].d = INF;
    id[f][u].same = INF;
    if(b != e) {
        int m = (b + e) >> 1;
        init(f, u << 1, b, m);
        init(f, (u << 1) + 1, m + 1, e);
    }
}
void update(int f, int u, int b, int e, int d) {
    if(d < id[f][u].d)
        id[f][u].d = d;
    if(id[f][u].b == b && e == id[f][u].e) {
        if(id[f][u].same > d)
            id[f][u].same = d;
        return;
    }
    int m = (id[f][u].b + id[f][u].e) >> 1;
    int lc = (u << 1), rc = (u << 1) + 1;
    if(e <= m) {
        update(f, lc, b, e, d);
    }
    else if(b > m)
        update(f, rc, b, e, d);
    else{
        update(f, lc, b, m, d);
        update(f, rc, m + 1, e, d);
    }
}
int query(int f, int u, int b, int e) {
    if((id[f][u].b == b && id[f][u].e == e)) {
        return id[f][u].d;
    }
    if(id[f][u].same <= id[f][u].d)
        return id[f][u].same;
    int m = (id[f][u].b + id[f][u].e) >> 1, x, y;
    int lc = u << 1, rc = (u << 1) + 1;
    if(e <= m) {
        x = query(f, lc, b, e);
        return id[f][u].same < x ? id[f][u].same : x;
    }
    else if(b > m) {
        x = query(f, rc, b, e);
        return id[f][u].same < x ? id[f][u].same : x;
    }
    else{
        x = query(f, lc, b, m);
        y = query(f, rc, m + 1, e);
        x = y < x ? y : x;
        return id[f][u].same < x ? id[f][u].same : x;
    }
}
int main() {
    int n, m, i, j, mn, a, b;
    while(~scanf("%d%d", &n, &m) && (n || m)) {
        for(i = 0; i < n; i++){
            for(j = 0; j < m; j++){
                scanf("%d", &t[i][j]);
            }
        }
        for(i = 0; i < n; i++){
            for(j = 0; j < m; j++){
                scanf("%d", &f[i][j]);
            }
        }
        init(0, 1, 0, m - 1);
        for(j = 0; j < m; j++) {
            a = j - f[0][j];
            b = j + f[0][j];
            if(a < 0)
                a = 0;
            if(b > m - 1)
                b = m - 1;
            update(0, 1, a, b, t[0][j]);
        }
        for(i = 1; i < n; i++) {
            init(i & 1, 1, 0, m - 1);
            for(j = 0; j < m; j++){
                a = j - f[i][j];
                b = j + f[i][j];
                if(a < 0)
                    a = 0;
                if(b > m - 1)
                    b = m - 1;
                mn = query(((~i) & 1), 1, a, b);
                update((i & 1), 1, a, b, t[i][j] + mn);
            }
        }
        printf("%d\n", query((~n) & 1, 1, 0, m - 1));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值