暑假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\n",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\n",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教的全局变量[甩锅]

 

posted @ 2019-07-21 20:19  _JSQ  阅读(233)  评论(0编辑  收藏  举报