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; }