LA5106: Let the light guide us

本文介绍了解决LA5106问题的一种算法思路,通过优化带绝对值的方程,采用树状数组维护最小值,将时间复杂度从O(nm²)降低到O(nmlogm)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LA5106: Let the light guide us

Dp·树状数组

题解:

裸方程:
|jk|<=magic[i1][k]+magic[i][j]时,

f[i][j]=min{f[i1][k]}+cost[i][j]

时间复杂度:O(nm2).

优化一下。像这种有绝对值的一定是规定大小,把绝对值去掉,然后正反做两遍。

k<=jkmagic[i1][k]<=j+magic[i][j]

然后用bit维护最小值即可。详见代码。

时间复杂度:O(nmlogm).

说一下我犯的zz错误:
1.先正着跑完所有行在反着跑所有行。。。
2.reverse没写足,有的没reverse回来。。。

Code:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
//#define debug
#ifdef debug
    #define D(x) cout<<#x<<" = "<<x<<"  "
    #define E cout<<endl
#else
    #define D(x)
    #define E
#endif
using namespace std;
const int N = 5005;
const int mxn = 200000;
const int INF = 0x3f3f3f3f;

int n,m,cost[105][N],magic[105][N],f[N],g[N],res[N],ans; 

struct BIT{
    int c[mxn+5], sz;
    void init(int _sz){ sz=_sz; memset(c,0x3f,sizeof(c)); }
    void ins(int x,int d){ if(x<1)x=1; while(x<=sz){ c[x]=min(c[x],d); x+=x&(-x); } }
    void clear(int x){ if(x<1)x=1; while(x<=sz){ c[x]=INF; x+=x&(-x); } }
    int mn(int x){ int ans=INF; while(x>0){ ans=min(ans,c[x]); x-=x&(-x); } return ans; }
} bit;


void dp(int i,int f[N]){
//  printf("dp: \n");
    for(int j=m;j>=1;j--){
        D(i); D(j); E;
//      printf("ins: pos=%d  d=%d\n",j-magic[i-1][j],res[j]);
        bit.ins(j-magic[i-1][j],res[j]);
        D(j+magic[i][j]); D(bit.mn(j+magic[i][j])); E;
        f[j]=bit.mn(j+magic[i][j])+cost[i][j];
        D(f[j]); E;
    }
    for(int j=1;j<=m;j++) bit.clear(j-magic[i-1][j]);
}

int main(){
    freopen("a.in","r",stdin);
#ifndef debug
    freopen("a.out","w",stdout);
#endif
    while(scanf("%d%d",&n,&m), n||m){
        D(n); D(m); E;
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) scanf("%d",&cost[i][j]);
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) scanf("%d",&magic[i][j]);
        bit.init(mxn); for(int i=1;i<=m;i++)res[i]=cost[1][i];
        for(int i=2;i<=n;i++){
            memset(f,0x3f,sizeof(f)); memset(g,0x3f,sizeof(g));
            dp(i,f); 
            reverse(cost[i]+1,cost[i]+1+m); 
            reverse(magic[i]+1,magic[i]+1+m);
            reverse(magic[i-1]+1,magic[i-1]+1+m);
            reverse(res+1,res+1+m);
            dp(i,g);
            reverse(magic[i]+1,magic[i]+1+m);
            reverse(res+1,res+1+m);
            for(int j=1;j<=m;j++)res[j]=min(f[j],g[m-j+1]);
        }
        ans=INF; for(int j=1;j<=m;j++) ans=min(ans,res[j]);
        printf("%d\n",ans); 
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值