HDU 3698 Let the light guide us 線段樹求區間優化dp

題意:有一個n*m(2<=n<=100 , 1<=m<=5000)的燈塔矩陣,每個燈塔有照耀周圍的亮度和維護的花費,現在要在每行取一個燈塔使得相鄰行的燈塔

        (如(i , j),(i+1 , k))滿足abs(j - k) <= light[ i ][ j ] + light[ i+1 ][ k ],問最少需要的花費是多少。

題解:很容易想到dp[ i ][ j ] = MIN(dp[ i ][ k ] + cost[ i ][ j ]),這樣的時間複雜度是O(n*m*m)會T,i-1行的燈塔的照耀範圍是一個區間,i 行的燈塔

         也是一個區間,這樣就轉化爲求區間最小值了,想到線段樹維護區間最小值即可。


Sure原創,轉載請註明出處

#include <iostream>
#include <cstdio>
#include <memory.h>
#define MIN(a , b) ((a) < (b) ? (a) : (b))
#define MAX(a , b) ((a) > (b) ? (a) : (b))
using namespace std;
const int inf = 1 << 29;
const int maxn = 102;
const int maxm = 5002;
struct node
{
    int l,r;
    int lazy,minval;
}seg[maxm << 2];
int light[maxn][maxm],cost[maxn][maxm],dp[maxm];
int m,n;

void read()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&cost[i][j]);
            if(i == 1) dp[j] = cost[1][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&light[i][j]);
        }
    }
    return;
}

void biuld(int l,int r,int num)
{
    seg[num].l = l;
    seg[num].r = r;
    seg[num].lazy = -1;
    seg[num].minval = inf;
    if(l == r) return;
    int mid = (l + r) >> 1;
    biuld(l , mid , num << 1);
    biuld(mid + 1 , r , num << 1 | 1);
    return;
}

void DOWN(int num)
{
    if(seg[num].lazy != -1)
    {
        if(seg[num << 1].lazy == -1 || seg[num << 1].lazy > seg[num].lazy)
        {
            seg[num << 1].lazy = seg[num].lazy;
        }
        if(seg[num << 1 | 1].lazy == -1 || seg[num << 1 | 1].lazy > seg[num].lazy)
        {
            seg[num << 1 | 1].lazy = seg[num].lazy;
        }
        seg[num << 1].minval = MIN(seg[num << 1].minval , seg[num].lazy);
        seg[num << 1 | 1].minval = MIN(seg[num << 1 | 1].minval , seg[num].lazy);
        seg[num].lazy = -1;
    }
    return;
}

void update(int l,int r,int val,int num)
{
    if(seg[num].l == l && seg[num].r == r)
    {
        if(seg[num].lazy == -1 || seg[num].lazy > val)
        {
            seg[num].lazy = val;
        }
        seg[num].minval = MIN(seg[num].minval , val);
        return;
    }
    DOWN(num);
    int mid = (seg[num].l + seg[num].r) >> 1;
    if(mid >= r) update(l , r , val , num << 1);
    else if(l > mid) update(l , r , val , num << 1 | 1);
    else
    {
        update(l , mid , val , num << 1);
        update(mid + 1 , r , val , num << 1 | 1);
    }
    seg[num].minval = MIN(seg[num << 1].minval , seg[num << 1 | 1].minval);
    return;
}

int query(int l,int r,int num)
{
    if(l == seg[num].l && r == seg[num].r)
    {
        return seg[num].minval;
    }
    DOWN(num);
    int mid = (seg[num].l + seg[num].r) >> 1;
    if(mid >= r) return query(l , r , num << 1);
    else if(l > mid) return query(l , r , num << 1 | 1);
    else return MIN(query(l , mid , num << 1) , query(mid + 1 , r , num << 1 | 1));
}

void solve()
{
    for(int i=2;i<=n;i++)
    {
        biuld(1 , m , 1);
        for(int j=1;j<=m;j++)
        {
            int l = MAX(1 , j - light[i-1][j]);
            int r = MIN(m , j + light[i-1][j]);
            update(l , r , dp[j] , 1);
        }
        for(int j=1;j<=m;j++)
        {
            int l = MAX(1 , j - light[i][j]);
            int r = MIN(m , j + light[i][j]);
            dp[j] = query(l , r , 1) + cost[i][j];
        }
    }
    int res = inf;
    for(int i=1;i<=m;i++)
    {
        res = MIN(res , dp[i]);
    }
    printf("%d\n",res);
    return;
}

int main()
{
    while(scanf("%d %d",&n,&m) && n+m)
    {
        read();
        solve();
    }
    return 0;
}

發佈了90 篇原創文章 · 獲贊 3 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章