zoukankan      html  css  js  c++  java
  • 暑假D9 T3elect(线段树优化DP)

    Description

    给出一个n行m列的矩阵,每个格子有一个花费TIJ,要求在每行选出恰好一个格子,使得这n个格子的Tij之和最小,每个格子还有一个权值Wij,对于相邻两行选择的格子(i,j1)和(i-1,j2),要求abs(j1-j2)<=W(i,j1)+W(i-1,j2)

    对于20% 的数据,保证 T = 1 ;
    对于另30% 的数据,保证 m < = 500 ;
    对于100% 的数据,保证 T<= 5, 2≤n≤100 ,1 <=m<=5000.
    0<= Tij 、Wij≤100000

    Input

    第一行个整数T,代表有T组数据 ,第二行两个整数 n,m;

    对于每一组数据:

    接下来 n行,每行 m个整数 Tij,再接下来 n行,每行 m个整数个整数 Wij

    题解

    暴力的DP还是可以想得到的,f[i][j]表示当前选到第i行,选第j个的最小值,直接先枚举行,再枚举列,最后再枚举从上一行哪一列转移过来,这样就有50pts

    #include<bits/stdc++.h>
    using namespace std;
    
    const int oo=10000005;
    const int maxn=105;
    const int maxm=5005;
    int T,n,m;
    int t[maxn][maxm];
    int w[maxn][maxm];
    int f[maxn][maxm];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    void nice(){
        read(n);read(m);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          read(t[i][j]),f[i][j]=oo;
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          read(w[i][j]);
        for(int i=1;i<=m;i++)
         f[1][i]=t[1][i];
        for(int i=2;i<=n;i++)
         for(int j=1;j<=m;j++)
          for(int k=1;k<=m;k++)
           if(abs(j-k)<=w[i][j]+w[i-1][k])
            f[i][j]=min(f[i][j],f[i-1][k]+t[i][j]);
        int ans=oo;
        for(int i=1;i<=m;i++) ans=min(ans,f[n][i]);
        printf("%d
    ",ans);
    }
    
    int main(){
        freopen("elect.in","r",stdin);
        freopen("elect.out","w",stdout);
        read(T);
        while(T--) nice();
    }
    View Code

    不过我只有20pts,因为题目只输入一次n,m.....

    那么正解是什么?

    abs在数轴上代表着两个数的距离,那么以j为圆心,Wij为半径的话就可以在数轴上划出一段区间,那么题目条件就变成了区间有重叠部分,只要j2所管辖区间有一点在j1区间,那么j1就可以从j2的答案转移,所以我们求j1的答案就变成了在区间内查找最小值,最后在求完这一行,在把每列的答案放进他所管辖区间。

    这就是区间查询和区间修改,赤果果的线段树基本操作,可是一开始谁又想得到呢......

    在弄完一行修改区间前,要先初始化把上一行的答案清掉。

    #include<bits/stdc++.h>
    using namespace std;
    //abs(j1-j2):j1和j2的距离
    //w(i,j1)可以看做一段区间
    //只要w(i-1,j2)有一个点在这段区间内就满足abs(j1-j2)<=w(i,j1)+2(i-1,j2) 
    #define re register
    const int oo=10000005;
    const int maxn=105;
    const int maxm=5005;
    int T,n,m,num,root;
    int a_l,a_r,a_v;
    int t[maxn][maxm];
    int lb[maxn][maxm],rb[maxn][maxm];
    int f[maxn][maxm];
    int mi[maxm<<1],ls[maxm<<1],rs[maxm<<1],tag[maxm<<1];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    __attribute__((optimize("-O2")))int min(int x,int y){return x<y ? x : y ;}
    __attribute__((optimize("-O2")))int max(int x,int y){return x>y ? x : y ;}
    
    __attribute__((optimize("-O2")))void build(int &rt,int l,int r){
        if(!rt) rt=++num;
        tag[rt]=mi[rt]=oo;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(ls[rt],l,mid);
        build(rs[rt],mid+1,r);
    }
    
    __attribute__((optimize("-O2")))inline void get_it(){
        for(re int i=1;i<=num;i++) tag[i]=mi[i]=oo;
    }
    
    __attribute__((optimize("-O2")))inline void put_tag(int rt,int v){
        mi[rt]=min(mi[rt],v);
        tag[rt]=min(tag[rt],v);
    }
    
    __attribute__((optimize("-O2")))inline void push_down(int rt){
        put_tag(ls[rt],tag[rt]);
        put_tag(rs[rt],tag[rt]);
        tag[rt]=oo;
    }
    
    __attribute__((optimize("-O2")))inline void update(int rt){
        mi[rt]=min(mi[ls[rt]],mi[rs[rt]]);
    }
    
    __attribute__((optimize("-O2")))void modify(int rt,int l,int r){
        if(a_l<=l&&r<=a_r){
            put_tag(rt,a_v);
            return ;
        }
        if(tag[rt]!=oo) push_down(rt);
        int mid=(l+r)>>1;
        if(a_l<=mid) modify(ls[rt],l,mid);
        if(mid<a_r) modify(rs[rt],mid+1,r);
        update(rt);
    }
    
    __attribute__((optimize("-O2")))int query(int rt,int l,int r){
        if(a_l<=l&&r<=a_r) return mi[rt];
        if(mi[rt]!=oo) push_down(rt);
        int mid=(l+r)>>1;
        int ans=oo;
        if(a_l<=mid) ans=min(ans,query(ls[rt],l,mid));
        if(mid<a_r) ans=min(ans,query(rs[rt],mid+1,r));
        return ans;
    }
    
    __attribute__((optimize("-O2")))inline void init(){
        for(int i=1;i<=m;++i){
          f[1][i]=t[1][i];
          a_l=lb[1][i];a_r=rb[1][i];a_v=f[1][i];
          modify(1,1,m);
        }
    }
    
    __attribute__((optimize("-O2")))inline void nice(){
        for(re int i=1;i<=n;++i)
         for(re int j=1;j<=m;++j)
           read(t[i][j]);
        for(re int i=1;i<=n;++i)
         for(re int j=1;j<=m;++j){
           int x;read(x);
           lb[i][j]=max(1,j-x);
           rb[i][j]=min(m,j+x);
         }
        get_it();
        init();
        for(re int i=2;i<=n;++i){
          for(re int j=1;j<=m;++j){
            a_l=lb[i][j];a_r=rb[i][j];
            f[i][j]=query(1,1,m)+t[i][j];
          }
          get_it();
          for(re int j=1;j<=m;++j){
            a_l=lb[i][j];a_r=rb[i][j];a_v=f[i][j];
            modify(1,1,m);
          }
        }
        printf("%d
    ",mi[1]);
    }
    
    int main(){
        freopen("elect.in","r",stdin);
        freopen("elect.out","w",stdout);
        read(T);read(n);read(m);
        num=root=0;build(root,1,m);
        while(T--) nice();
    }
    /*
    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
    */
    View Code

    我写的线段树太垃圾了,评测姬跑不过,时限开大还是跑不过....就只好开O2[呸,不要脸]

    听说将修改区间的L,R传入函数会快一些,那可能要怪yyr教的全局变量[甩锅]

     

  • 相关阅读:
    JDK 5 ~ 10 新特性倾情整理!
    软件设计师-面向对象
    软件设计师-系统开发基础
    软件设计师-网络与信息安全
    软件设计师-数据库系统
    软件设计师-操作系统
    软件设计师-计算机系统知识
    DbParameter
    软件设计师-算法
    android studio快捷键
  • 原文地址:https://www.cnblogs.com/sto324/p/11222526.html
Copyright © 2011-2022 走看看