线段树优化dp(elect选择)

好几天以前的考试题,现在才想起来调。。

题意

选格子,要求选出的权值最小,但每次选的格子范围有限制:abs(j-k)<=w[i][j]+w[i-1][k]   w是其另一个值

暴力:O(n*m*m*T)

优化:对于将要选的一行格子,将其上一行格子能够覆盖的范围处理出来,然后选这一行时,希望较快的得到在这个格子可以选的范围里面,权值最小的一格

于是对每一行用线段树维护最小值,到了下一行就把线段树清空,重新modify

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define N 105
#define M 5005
#define inf 0x7f7f7f
int a[N][M],dp[N][M],minn[M*4],fl[M*4],l[N][M],r[N][M];
void update(int s)
{ minn[s]=min(minn[s<<1],minn[s<<1|1]); }
void pushdown(int s)
{
    if(fl[s]==inf) return ;
    minn[s<<1]=min(minn[s<<1],fl[s]);
    fl[s<<1]=min(fl[s<<1],fl[s]);
    minn[s<<1|1]=min(minn[s<<1|1],fl[s]);
    fl[s<<1|1]=min(fl[s<<1|1],fl[s]);
    fl[s]=inf;
}
void build(int s,int l,int r)
{
    fl[s]=inf;
    if(l==r) { minn[s]=inf;  return ; }
    build(s<<1,l,mid); build(s<<1|1,mid+1,r);
    update(s);
}
void modify(int s,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R){
        minn[s]=min(minn[s],v);//一定要记得更新!! 
        fl[s]=min(fl[s],v);
        return ;
    }
    pushdown(s);//modify和query都有标记下传 
    if(L<=mid) modify(s<<1,l,mid,L,R,v);
    if(R>mid)  modify(s<<1|1,mid+1,r,L,R,v);
    update(s);
}
int query(int s,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return minn[s];
    pushdown(s);
    int ans=inf;
    if(L<=mid) ans=min(ans,query(s<<1,l,mid,L,R));
    if(R>mid)  ans=min(ans,query(s<<1|1,mid+1,r,L,R));
    return ans;
}
int main()
{
    freopen("elect.in","r",stdin);
    freopen("elect.out","w",stdout);
    int T,n,m;
    scanf("%d%d%d",&T,&n,&m);
    while(T--){
        int ans=0x7f7f7f;
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
             int x;
             scanf("%d",&x);
             l[i][j]=max(1,j-x);//对于每一行的每一个点求出能覆盖的最远距离 用线段树维护区间最值 
             r[i][j]=min(m,j+x);
         }
        build(1,1,m);
        for(int i=1;i<=m;i++)
         dp[1][i]=a[1][i],modify(1,1,m,l[1][i],r[1][i],dp[1][i]);
        for(int i=2;i<=n;i++){
             for(int j=1;j<=m;j++)
               dp[i][j]=query(1,1,m,l[i][j],r[i][j])+a[i][j];
             build(1,1,m);//一行对应一个线段树!! 一行用完后要重建 
             for(int j=1;j<=m;j++)
             modify(1,1,m,l[i][j],r[i][j],dp[i][j]);
        }
        for(int i=1;i<=m;i++) ans=min(ans,dp[n][i]);
        printf("%d\n",ans);
    }
}
/*
1
3 5
9 5 3 8 7
8 2 6 8 9
1 9 7 8 6

0 1 0 1 2
1 0 2 1 1
0 2 1 0 2
*/

 

转载于:https://www.cnblogs.com/mowanying/p/11222832.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值