題意:有一個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;
}