题意:给一个值矩阵,给一个距离矩阵。(100*5000)
在值矩阵每行选一个格,要求相邻两行所选格的列距离小于等于距离矩阵和。
求所有选定格的最小值。
dp方程:
dp[i][j] = min{dp[i-1][k]+val[i][j]} dis[i-1][k]+dis[i][j] >= |k-j|;
显然状态是N*M,转移是 M,复杂度N*M*M,直接搞超时。
优化dp:
线段树
四边形
单调队列
这里选线段树...
将dp[i-1][j]插入区间[j-dis[i-1][j], j+dis[i-1][j]];
对于dp[i][j]查找区间[j-dis[i][j], j+dis[i][j]];
维护区间最小值
直接做法,如果插入的数值越来越小,区间是最大区间,则退化...还是超时...
segmin是区间最小值,segcov是覆盖整个区间的最小值,懒标记处理一下....
延伸:
如果要求首行和尾行也必须满足条件,怎么弄...
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
const int N = 111, M = 5555, inf = 0x7fffffff;
int cost[N][M] = {0}, scal[N][M] = {0}, dp[N][M] = {0};
int segmin[M<<2], segcov[M<<2];
void down(int L, int R, int rt)
{
if(segcov[rt<<1] > segcov[rt] || segcov[rt<<1] == -1) {
segcov[rt<<1] = segcov[rt];
}
if(segcov[rt<<1|1] > segcov[rt] || segcov[rt<<1|1] == -1) {
segcov[rt<<1|1] = segcov[rt];
}
if(segmin[rt<<1] > segcov[rt<<1] || segmin[rt<<1] == -1) {
segmin[rt<<1] = segcov[rt<<1];
}
if(segmin[rt<<1|1] > segcov[rt<<1|1] || segmin[rt<<1|1] == -1) {
segmin[rt<<1|1] = segcov[rt<<1|1];
}
segcov[rt] = -1;
}
int que(int l, int r, int L, int R, int rt)
{
if(l <= L && R <= r) {
return segmin[rt];
}
int m = (L + R)>>1, ret = inf, tmp;
if(l <= m) {
if(segcov[rt] != -1) {
down(L, R, rt);
}
tmp = que(l, r, L, m, rt<<1);
if(ret > tmp) {
ret = tmp;
}
}
if(r > m) {
if(segcov[rt] != -1) {
down(L, R, rt);
}
tmp = que(l, r, m+1, R, rt<<1|1);
if(ret > tmp) {
ret = tmp;
}
}
return ret;
}
void ins(int v, int l, int r, int L, int R, int rt)
{
if(segmin[rt] > v || segmin[rt] == -1) {
segmin[rt] = v;
}
if(l <= L && R <= r) {
if(segcov[rt] > v || segcov[rt] == -1) {
segcov[rt] = v;
}
return ;
}
int m = (L + R)>>1;
if(l <= m) {
if(segcov[rt] != -1) {
down(L, R, rt);
}
ins(v, l, r, L, m, rt<<1);
}
if(r > m) {
if(segcov[rt] != -1) {
down(L, R, rt);
}
ins(v, l, r, m+1, R, rt<<1|1);
}
}
int main()
{
int n, m, i, j;
while(~scanf("%d%d", &n, &m) && n+m) {
for(i = 1; i <= n; ++i) {
for(j = 1; j <= m; ++j) {
scanf("%d", &cost[i][j]);
}
}
for(i = 1; i <= n; ++i) {
for(j = 1; j <= m; ++j) {
scanf("%d", &scal[i][j]);
}
}
memset(segmin, 0, sizeof(segmin));
memset(segcov, -1, sizeof(segcov));
int l, r;
for(i = 1; i <= n; ++i) {
for(j = 1; j <= m; ++j) {
l = j - scal[i][j];
r = j + scal[i][j];
if(l < 1) { l = 1; }
if(r > m) { r = m; }
dp[i][j] = cost[i][j] + que(l, r, 1, m, 1);
}
for(j = 1; j < M<<2; ++j) {
segmin[j] = inf;
segcov[j] = -1;
}
for(j = 1; j <= m; ++j) {
l = j - scal[i][j];
r = j + scal[i][j];
if(l < 1) { l = 1; }
if(r > m) { r = m; }
ins(dp[i][j], l, r, 1, m, 1);
}
}
printf("%d\n", segmin[1]);
}
return 0;
}