http://acm.hdu.edu.cn/showproblem.php?pid=3698
HDU 3698 Let the light guide us (DP+线段树) #by Plato
题意:一个N*M的矩阵,在每行上选一个点(建灯塔),每个点都要各自对应的代价c和灯塔的光亮f。同时相邻两行的点要满足 |j-k|≤f(i,j)+f(i+1,k)。问最小代价是多少。
Idea:
很容易想到DP方程:f[i,j] = min{f[i-1,k]} + c[i,j];
k 的范围:|j-k|≤f(i,j)+f(i+1,k)
直接这样做的话,O(N*M*M),TLE节奏。所以需要优化。
可以看出,k是在一个区间里的最值,单调队列?线段树?
应该是线段树,但是树怎么建,区间怎么确定?这个我也一直想不清楚,后来看题解了好久才明白。(作下几何图试试)
建树要注意的地方比较多了(可能只针对我这样的菜菜),需要lazy标志,初始化,lazy的初值与更新。
建树,1-M的区间,每个结点保存此区间的最小值(上一行能照到这个结点的)。
(要去上课了,详细的下次补全了)
题目昨天卡了一天,主要是线段树写挫了,调了好久,今早才想起一个错误,改了,终于A了。。。
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>
using namespace std;
const int INF = (1<<31)-1;
int c[109][5009],f[109][5009];
int g[109][5009];
struct node
{
int l,r,lazy,min;
}t[5009*4];
int min_value;
void pushdown(int k)
{
if (t[k].lazy != INF) {
t[k<<1].lazy = min(t[k<<1].lazy,t[k].lazy);
t[k<<1|1].lazy = min(t[k<<1|1].lazy,t[k].lazy);
t[k<<1].min = min(t[k].lazy,t[k<<1].min);
t[k<<1|1].min = min(t[k].lazy,t[k<<1|1].min);
t[k].lazy = INF;//t[k].lazy = 0;
}
}
void construct(int l,int r,int k,int ii)
{
t[k].l = l;
t[k].r = r;
t[k].lazy = INF;
//t[k].lazy = 0;
t[k].min = INF;
if (l == r){
t[k].min = g[ii][l];
return;
}
int mid = (l+r)>>1;
construct(l,mid,k<<1,ii);
construct(mid+1,r,k<<1|1,ii);
t[k].min = min(t[k<<1].min,t[k<<1|1].min);
}
void updata(int k,int left,int right,int value)
{
t[k].min = min(value,t[k].min);
if (t[k].l == left && t[k].r == right){
//t[k].min = min(value,t[k].min);
t[k].lazy = min(t[k].lazy,value);
//t[k].lazy = value;
return;
}
pushdown(k);
int mid = (t[k].l+t[k].r)>>1;
if (right <= mid) updata(k<<1,left,right,value);
else
if (left > mid) updata(k<<1|1,left,right,value);
else {
updata(k<<1,left,mid,value);
updata(k<<1|1,mid+1,right,value);
}
}
void query(int k,int left,int right)
{
if (t[k].l == left && t[k].r == right){
if (t[k].min < min_value) min_value = t[k].min;
return;
}
pushdown(k);
int mid = (t[k].l+t[k].r)>>1;
if (right <= mid) query(k<<1,left,right);
else
if (left > mid) query(k<<1|1,left,right);
else {
query(k<<1,left,mid);
query(k<<1|1,mid+1,right);
}
}
int main()
{
freopen("test.txt","r",stdin);
int N,M;
while (scanf("%d%d",&N,&M),N+M)
{
for (int i = 1;i <= N;i++)
for (int j = 1;j <= M;j++)
scanf("%d",&c[i][j]);
for (int i = 1;i <= N;i++)
for (int j = 1;j <= M;j++)
scanf("%d",&f[i][j]);
memset(g,-1,sizeof(g));
for (int i = 1;i <= M;i++)
g[1][i] = c[1][i];
for (int i = 2;i <= N;i++)
{
construct(1,M,1,i-1);
for (int j = 1;j <= M;j++)
{
int L = j - f[i-1][j] > 0 ? j - f[i-1][j] : 1;
int R = j + f[i-1][j] < M ? j + f[i-1][j] : M;
updata(1,L,R,g[i-1][j]);
}
for (int j = 1;j <= M;j++)
{
int L = j - f[i][j] > 0 ? j - f[i][j] : 1;
int R = j + f[i][j] < M ? j + f[i][j] : M;
min_value = INF;
query(1,L,R);
g[i][j] = min_value + c[i][j];
}
}
int minx = INF;
for (int i = 1;i <= M;i++)
{
if (g[N][i] < minx && g[N][i] != -1) minx = g[N][i];
}
printf("%d\n",minx);
}
return 0;
}