您的位置:首页 > 其它

Codeforces 677D 二维线段树 + 重要技巧

2016-06-16 21:15 513 查看
链接:http://codeforces.com/problemset/problem/677/D

到处看题解,用两种方法A了这道题

方法一:

我们到了点(x0,y0) 取得了这一点的钥匙a    可以拿着这个点的最短距离去更新同一行,手上有钥匙a的时候的最短距离

当我们需要求取得(x1, y1)处的钥匙a+1的最短距离时, 可以用与这个点同列的有钥匙a状态的点来更新

概括的说将巧妙地哈曼顿距离拆成与x,y轴分别平行的两段

这样的复杂度是 (n+m)*(nm)   

有个细节:

如果我没有看过题解代码的话,在求具有钥匙a的距离时

①     dis[n*m]
[m]的数组      显然MLE, 而且初始化是 8e9的 memset(dis, 0x3f, sizeof dis); TLE

②     开dis
[m]                    每一次钥匙值改变都memset一下           同样TLE

考虑300 * 300的地图 并且p == 90000

这个做法的话每次需要用到的空间是300个int, 每次都要memset显然时间浪费巨大

做法:    用mark[300][300]保存 上一次更新到这个点时手中有的钥匙   

如果与这次更新的钥匙值相同    距离取小值, 否则距离换成新的, mark[i][j]随之改变

方法二:


以下公式算抄别人的……

if (key[i][j] + 1 == key[x][y]) dp[x][y] = min(dp[x][y], dp[i][j] + abs(x-i) + abs(y-j));

如果i, j在x, y左上方    dp[x][y] = min(dp[x][y],
dp[i][j] - i - j
+ x + y);

同理右上 左下 右下,将整个地图裂成了4块

有一种似曾相识的赶脚……这不是单调队列优化dp的套路吗

用四棵二维线段树维护红色部分  

处理dp[x][y]时,以左上举例:

线段树维护 dp[i][j] - i - j 的最小值 query出 (1, 1) 至 (x,y) 的最小值 minv

minv + x + y 就是暂时的最小值

分别求出四块的得出dp[x][y]

递推公式求最值的题目,没有想法的话一定要试试能不能把公式分解啊!!

本来还没有写方法一的时候,   难到每次都要把树全部重新建一遍吗? TLE啊

后来发现 打个mark就行了……

玩了下class…好像不是很成功唉

<span style="font-size:14px;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 3e2 + 4;
int ans[maxn][maxn], dis[maxn][maxn], mark[maxn][maxn];
vector<int> node[maxn*maxn];
int n, m, p, x, y;
int main(){
ios::sync_with_stdio(false);
int i, j, k, kase;
memset(mark, -1, sizeof mark);
memset(ans, 63, sizeof ans);
memset(dis, 0, sizeof dis);
cin >> n >> m >> p;
for (i = 0; i <= p; ++i) node[i].clear();
for (i = 0; i < n; ++i)
for (j = 0; j < m; ++j){
cin >> x;
node[x].push_back(i*m + j);
}
node[0].push_back(0);
for (i = 0; i <= p; ++i){
for (j = 0; j < int(node[i].size()); ++j){
x = node[i][j] / m;
y = node[i][j] % m;
//			cout << x << ' ' << y << endl;
if (x == 0 && y == 0 && i != 0) ans[x][y] = 0x3f3f3f3f;
for (k = 0; k < n; ++k)
if (mark[k][y] == i - 1) ans[x][y] = min(ans[x][y], dis[k][y] + int(abs(k - x)));
//				cout << ans[x][y] << endl;
}

for (j = 0; j < int(node[i].size()); ++j){
x = node[i][j] / m;
y = node[i][j] % m;
//			cout << x << ' ' << y << ' ' << ans[x][y] << endl;
for (k = 0; k < m; ++k)
if (mark[x][k] != i){
mark[x][k] = i;
dis[x][k] = ans[x][y] + abs(y - k);
}
else dis[x][k] = min(dis[x][k], ans[x][y] + int(abs(y - k)));
}
//		cout << endl;
}
//	cout << x << ' ' << y << endl;
cout << ans[x][y] << endl;
return 0;
}</span>


/*
玩了发纯虚函数, 一开始还以为会有用的
训练了二维线段树
1.通过给点做标记 而不用每一次大循环都把树重新清空一下
2.求(x-i) + (y-j)的最大值的方法 dp的双端队列优化也有这一点有趣啊
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define lft pos << 1
#define rght (pos << 1) + 1
const int maxn = 3e2 + 4;
const int inf = 0x3f3f3f3f;
int l[maxn << 2], r[maxn << 2], u[maxn << 2], e[maxn << 2];
void build(int pos, int L, int R){
l[pos] = L, r[pos] = R;
if (L == R) return;
build(lft, L, (L + R) / 2);
build(rght, (L + R) / 2 + 1, R);
return;
}
void build2(int pos, int up, int down){
u[pos] = up, e[pos] = down;
if (up == down) return;
build2(lft, up, (up + down) / 2);
build2(rght, (up + down) / 2 + 1, down);
return;
}
//好像不能通过循环求出最大值啊 伤心
class tree{
protected:
int minv[maxn << 2][maxn << 2];
int mark[maxn << 2][maxn << 2];
public:
virtual void update(int a, int num, int pos, int x, int y) = 0;
virtual void query(int a, int pos, int L, int R, int down, int up) = 0;
tree(){
memset(minv, 0, sizeof minv);
memset(mark, -1, sizeof mark);
}
};
int ans, n, m, p;
class Tree: public tree{
public:
void update2(int a, int num, int rows, int pos, int y){
if (mark[rows][pos] != a){
mark[rows][pos] = a;
minv[rows][pos] = num;
}
minv[rows][pos] = min(minv[rows][pos], num);
if (u[pos] == e[pos]) return;
if (e[lft] >= y) update2(a, num, rows, lft, y);
else update2(a, num, rows, rght, y);

}
void update(int a, int num, int pos, int x, int y){
update2(a, num, pos, 1, y);
if (l[pos] == r[pos]) return;
if (r[lft] >= x) update(a, num, lft, x, y);
else update(a, num, rght, x, y);
}
void query2(int a, int rows, int pos, int down, int up){// up < down
if (mark[rows][pos] != a || ans <= minv[rows][pos]) return;
// cout << "query2" << ' ' << pos << ' ' << u[pos] << ' ' << e[pos] << endl;
if (u[pos] >= up && e[pos] <= down){
ans = minv[rows][pos];
// cout << "successful\n";
return;
}
if (u[pos] > down || e[pos] < up) return;
query2(a, rows, lft, down, up);
query2(a, rows, rght, down, up);
}
void query(int a, int pos, int L, int R, int down, int up){
// cout << pos << endl;
// cout << L << ' ' << l[pos] << " " << R << ' ' << r[pos] << endl ;
if (l[pos] >= L && r[pos] <= R){
query2(a, pos, 1, down, up);
return;
}
if (l[pos] > R || r[pos] < L) return;
query(a, lft, L, R, down, up);
query(a, rght, L, R, down, up);
}
Tree():tree(){
}
}T[4];
int d[maxn][maxn], x, y;
vector<int> node[maxn*maxn];
int main(){
ios::sync_with_stdio(false);
int i, j, k, kase;
cin >> n >> m >> p;
build(1, 1, n);
build2(1, 1, m);
for (i = 0; i <= p; ++i) node[i].clear();
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j){
cin >> x;
node[x].push_back(i*m + j);
}
node[0].push_back(m+1);
for (i = 0; i <= p; ++i){
for (j = 0; j < int(node[i].size()); ++j){
x = (node[i][j] - 1) / m;
y = (node[i][j] - 1) % m + 1;
{
ans = inf;
// cout << "lft :=" << 1 << " rght :=" << x << endl;
T[0].query(i-1, 1, 1, x, y, 1);
// cout << x << ' ' << y << ' ' << i << ' ' << ans << endl << endl;

// getchar();getchar();getchar();
d[x][y] = ans + x + y;
ans = inf;
T[1].query(i-1, 1, x, n, y, 1);
d[x][y] = min(d[x][y], ans + y - x);
ans = inf;
T[2].query(i-1, 1, 1, x, m, y);
d[x][y] = min(d[x][y], ans + x - y);
ans = inf;
T[3].query(i-1, 1, x, n, m, y);
d[x][y] = min(d[x][y], ans - y - x);
}
if (i == 0) d[x][y] = 0;
// cout << "attention!!!! " << d[x][y] << endl;
}

for (j = 0; j < int(node[i].size()); ++j){
x = (node[i][j] - 1) / m;
y = (node[i][j] - 1) % m + 1;
{
T[0].update(i, d[x][y] - x - y, 1, x, y);
T[1].update(i, d[x][y] + x - y, 1, x, y);
T[2].update(i, d[x][y] - x + y, 1, x, y);
T[3].update(i, d[x][y] + x + y, 1, x, y);
}
}
}
// cout << x << ' ' << y << endl;
cout << d[x][y] << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: