线段树优化dp(elect选择)

匿名 (未验证) 提交于 2019-12-02 23:49:02

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

题意

暴力: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 */

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!