Codeforces Round 355 (Div. 2) D. Vanya and Treasure(dp、二维BIT优化)
题意:
$N,M\le 300,P\le N\times M,给定一个N\times M图,每个格子A_{ij}是1\sim P的数字$
$从(1, 1)出发,两个格子的距离定义为曼哈顿距离,按顺序取1\sim P的数字$
$问最短路是多少$
分析:
$我们有一个显然的dp状态,f[x][y]:=到达(x, y)这个格子,并且取到数字的最短路$
$f[x_2][y_2]=min\{ f[x_1][y_1]+dis((x_1,y_1), (x_2,y_2)) \},A[x_1][y_1]=i-1,A[x_2][y_2]=i$
$裸的转移是O(nm)的,总复杂度就是O((nm)^2)了,显然是不行的,考虑优化$
$优化一:$
$令cnt[i]:=i这个数字的个数,当cnt[i-1]\times cnt[i]\le n\times m的时候直接更新,否则bfs一波$
$这个时间复杂度是O(nm\sqrt{nm}),如果有人懂详细证明请教我。。$
$优化二:$
$我们展开这个dp转移方程:$
$f[x_2][y_2]=min\{ f[x_1][y_1]+abs(x_1-x_2)+abs(y_1-y_2) \}$
$发现对于曼哈顿距离其实有4种情况,根据x和y的大小关系,方便起见画个图:$
$A点为我们当前要更新的点,B0\sim B3为代表根据x和y大小划分的4个区域$
$我们可以得到新的转移方程:$
$UL - f[x_2][y_2] = min \{ f[x_1][y_1] - x_1 - y_1 + x_2 + y_2 \}, B0区域$
$UR - f[x_2][y_2] = min \{ f[x_1][y_1] + x_1 - y_1 - x_2 + y_2 \}, B1区域$
$BR - f[x_2][y_2] = min \{ f[x_1][y_1] + x_1 + y_1 - x_2 - y_2 \}, B2区域$
$BL - f[x_2][y_2] = min \{ f[x_1][y_1] - x_1 + y_1 + x_2 - y_2 \}, B3区域$
$对于当前点A(x_2, y_2)来说,转移方程中关于x_2和y_2的部分是常数$
$对于f[x_1][y_1]以及x_1和y_1的部分来说,是矩形区域,我们可以用二维线段树来维护$
$观察这4个区域,发现不是前缀区域就是后缀区域,所以我们用4个二维BIT就可以了$
$维护后缀区域只要转化一下就好了,具体见代码$
代码一:
//
// Created by TaoSama on 2016-06-02
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 300 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m, p;
int s[N][N];
typedef pair<int, int> P;
vector<P> G[N * N];
int f[N][N];
void getMin(int& x, int y) {
if(x > y) x = y;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
clock_t _ = clock();
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
scanf("%d", s[i] + j);
G[s[i][j]].push_back({i, j});
f[i][j] = s[i][j] == 1 ? i + j - 2 : INF;
}
}
for(int i = 1; i < p; ++i) {
vector<P>& cur = G[i], &nxt = G[i + 1];
int delta = cur.size() * nxt.size();
if(delta <= n * m) {
for(P& u : cur) {
for(P& v : nxt) {
int d = abs(u.first - v.first) + abs(u.second - v.second);
getMin(f[v.first][v.second], f[u.first][u.second] + d);
}
}
} else {
static vector<P> q; q.clear();
static int d[N][N], in[N][N];
memset(d, 0x3f, sizeof d);
memset(in, false, sizeof in);
for(P& u : cur) {
q.push_back(u);
in[u.first][u.second] = true;
d[u.first][u.second] = f[u.first][u.second];
}
static int dir[][2] = { -1, 0, 0, -1, 1, 0, 0, 1};
for(int j = 0; j < q.size(); ++j) {
P u = q[j];
in[u.first][u.second] = false;
for(int k = 0; k < 4; ++k) {
P v = u;
v.first += dir[k][0];
v.second += dir[k][1];
if(!s[v.first][v.second]) continue;
if(d[v.first][v.second] > d[u.first][u.second] + 1) {
d[v.first][v.second] = d[u.first][u.second] + 1;
if(!in[v.first][v.second]) {
in[v.first][v.second] = true;
q.push_back(v);
}
}
}
}
for(P& v : nxt) f[v.first][v.second] = d[v.first][v.second];
}
}
int x = G[p][0].first, y = G[p][0].second;
printf("%d\n", f[x][y]);
#ifdef LOCAL
printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
return 0;
}
代码二:
//
// Created by TaoSama on 2016-06-02
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
#include <bitset>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 300 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
int n, m, p;
int s[N][N], f[N][N];
typedef pair<int, int> P;
vector<P> G[N * N];
struct BIT {
int n, b[N][N];
int timStp, vis[N][N];
void init(int _n) {
n = _n;
timStp = 1;
memset(vis, 0, sizeof vis);
}
void newOne() {
++timStp;
}
void update(int x, int y, int v) {
for(int i = x; i <= n; i += i & -i) {
for(int j = y; j <= n; j += j & -j) {
if(vis[i][j] != timStp) vis[i][j] = timStp, b[i][j] = INF;
b[i][j] = min(b[i][j], v);
}
}
}
int query(int x, int y) {
int ret = INF;
for(int i = x; i; i -= i & -i)
for(int j = y; j; j -= j & -j)
if(vis[i][j] == timStp) ret = min(ret, b[i][j]);
return ret;
}
} bit[4];
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
clock_t _ = clock();
scanf("%d%d%d", &n, &m, &p);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
scanf("%d", s[i] + j);
G[s[i][j]].push_back({i, j});
}
}
memset(f, 0x3f, sizeof f);
int nm = max(n, m);
for(int i = 0; i < 4; ++i) bit[i].init(nm);
//UL - f[x2][y2] = min { f[x1][y1] - x1 - y1 + x2 + y2 }
//UR - f[x2][y2] = min { f[x1][y1] + x1 - y1 - x2 + y2 }
//BR - f[x2][y2] = min { f[x1][y1] + x1 + y1 - x2 - y2 }
//BL - f[x2][y2] = min { f[x1][y1] - x1 + y1 + x2 - y2 }
for(int i = 0; i < G[1].size(); ++i) {
int x = G[1][i].first, y = G[1][i].second;
f[x][y] = x + y - 2;
bit[0].update(x, y, f[x][y] - x - y);
bit[1].update(nm - x + 1, y, f[x][y] + x - y);
bit[2].update(nm - x + 1, nm - y + 1, f[x][y] + x + y);
bit[3].update(x, nm - y + 1, f[x][y] - x + y);
}
for(int i = 2; i <= p; ++i) {
for(int j = 0; j < G[i].size(); ++j) {
int x = G[i][j].first, y = G[i][j].second, val = INF;
val = min(val, bit[0].query(x, y) + x + y);
val = min(val, bit[1].query(nm - x + 1, y) - x + y);
val = min(val, bit[2].query(nm - x + 1, nm - y + 1) - x - y);
val = min(val, bit[3].query(x, nm - y + 1) + x - y);
f[x][y] = val;
}
for(int j = 0; j < 4; ++j) bit[j].newOne();
for(int j = 0; j < G[i].size(); ++j) {
int x = G[i][j].first, y = G[i][j].second;
bit[0].update(x, y, f[x][y] - x - y);
bit[1].update(nm - x + 1, y, f[x][y] + x - y);
bit[2].update(nm - x + 1, nm - y + 1, f[x][y] + x + y);
bit[3].update(x, nm - y + 1, f[x][y] - x + y);
}
}
int x = G[p][0].first, y = G[p][0].second;
printf("%d\n", f[x][y]);
#ifdef LOCAL
printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
#endif
return 0;
}