距离问题常见解法

欧几里得距离:\(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)
曼哈顿距离:\(|x_1-x_2|+|y_1-y_2|\)
切比雪夫距离:\(\max(|x_1-x_2|,|y_1-y_2|)\)
常见套路:将二维坐标系上 \((x,y) \rightarrow (x+y,x-y)\),原来任意一点的曼哈顿距离变成切比雪夫距离;将二维坐标系上 \((x,y) \rightarrow (\cfrac{x+y}{2},\cfrac{x-y}{2})\),原来任意一点的切比雪夫距离变成曼哈顿距离。
曼哈顿距离 \(\le k\) 的点构成一个正方形,正方形的边与 \(x\) 轴夹角为 \(45 °\)
切比雪夫距离 \(\le k\) 的点构成一个正方形,正方形的边与坐标轴夹角为 \(90 °\)\(0 °\)。(这样的正方形可以更好地处理,比如二维前缀和统计正方形内的元素个数)

HDU3841

给定若干个点 \(x_i,y_i\)\(q\),求地图上一个点,使得它和最多个点的曼哈顿距离不大于 \(q\)
\(x_i,y_i \le 1000, q \le 10^6\)

先将曼哈顿距离转化为切比雪夫距离,预处理前缀和然后暴力枚举每一个点,\(O(1)\) 查询 \((x_i + d, y_i + d), (x_i - d, y_i - d)\) 的点的个数。

注意转化之后 \(x \in [0,2000],y \in[-1000, 1000]\)

#include <bits/stdc++.h>
using namespace std;
#define f(i, a, b) for (int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(), i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
int dx, dy, n, m;
int s[2010][2010];
int ansx, ansy;
bool cmp(pii x, pii y) {
    if(x.second != y.second) return x.second < y.second;
    else return x.first < y.first;
}
int px(int x) {if(x > 2000) x = 2000; if(x < 0) x = 0; return x;}
int py(int y) {if(y > 1000) y = 1000; if(y < -1000) y = -1000; return y;}
int main()
{
    int t = 0;
    while (cin >> dx >> dy >> n >> m && dx != 0)
    {   
        memset(s, 0, sizeof(s));
        ++t;
        f(i, 1, n)
        {
            int x, y;
            cin >> x >> y;
            s[x+y][x-y+1001]++;
        }
        f(i, 1, 2001) f(j, 1, 2001) {
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + s[i][j];
        }
        
        cout << "Case "<<t<<":\n";
        f(i, 1, m)
        {
            int mx = 0;
            int d; cin >> d; if(d > 2000) d = 2000;
            f(x, 1, 2000) f(y, -1000, 1000) {
                if(x + y < 2 || x + y > 2*dx || x - y < 2 || x - y > 2*dy || (x + y) & 1) {
                    continue;
                }
                int tmp = s[px(x + d)][py(y + d) + 1001] - s[px(x + d)][py(y - d - 1) + 1001] - s[px(x - d - 1)][py(y + d) + 1001] + s[px(x - d - 1)][py(y - d - 1) + 1001];
                if(tmp > mx) {
                    mx = tmp;
                    ansx = (x+y)/2; ansy = (x-y)/2;
                }
                else if(tmp == mx) {
                    if(ansy > (x-y)/2) {ansx = (x+y)/2; ansy = (x-y)/2;}
                    else if(ansy == (x-y)/2 && ansx > (x+y)/2) {ansx = (x+y)/2; ansy = (x-y)/2;}
                }
            }
            cout << mx << " ("<<ansx << ","<<ansy << ")\n";
        }
    }
    return 0;
}

HDU7172

平面上有 \(n\) 个点,第 \(i\) 个点的坐标为 \((x_i,y_i)\),并有一个参数 \(w_i\),表示另一个点 \(j\) 到这个点的收益为 \(\min(dis_{i,j},w_i)\)。有 \(q\) 次询问,每次给出一个点的坐标,求这个点到哪个点的收益最大。
\(n,q \le 5 \times 10^5,x_i,y_i,w_i \le 10^9\)

赛时看到这个题,首先想到的是“曼哈顿距离转化为切比雪夫距离”(总是有好性质)然后我们惊讶地发现,最大收益的式子变成了:

\[\max \limits_{i}\{\max(|x_i-x|,|y_i-y|,w_i)\} \]

这个东西 \(\max\)\(\max\),可以两个维度分开考虑!(切比雪夫就是神)
那么我们问题转化为了:给定 \(x\),求 \(\max \limits_{i}\{\max(|x_i-x|,w_i)\}\)
发现对于每一个 \(i\),当 \(x \le x_i-w_i\) 或者 \(x \ge x_i+w_i\) 的时候,生效的是 \(w_i\);反之是距离。可以用双指针,先进行离散化,然后 \(i\) 跑一遍离散化的值域,并维护两个集合 \(s\)\(t\)。一开始集合 \(s\) 中有所有 \(w\) 的值。遇到 \(x_i-w_i\) 的时候把 \(s\)\(w_i\) 弹出,在 \(t\) 中插入 \(x_i\),遇到 \(x_i+w_i\) 相反。遇到询问的时候,它的答案就是 \(\max\{s_{\max},\max\{|x_q-t_{\max}|,|x_q-t_{\min}|\}\}\)

时间复杂度 \(O(n \log n)\),使用 multiset 常数比较大。很遗憾被卡到了 \(4s\) 左右,还写了将近 \(4k\) 的代码。

我们先看看标算,再考虑卡卡本做法的常数。

标算的想法出发点是,发现去掉 \(w_i\) 之后是一个经典题:求平面上距离给定点的曼哈顿距离最大的点。这是可以 \(O(n)\) 预处理,\(O(1)\) 查询的东西:

\[\max\{|x_i-x|+|y_i-y|\} \\ =\max\{\max\{x_i-x,x-x_i\}+\max\{y_i-y,y-y_i\}\} \\ =\max\{x_i-x+y_i-y,x-x_i+y_i-y,x-x_i+y_i-y,x_i-x+y-y_i\} \]

思路还是把绝对值换成 \(\max\),但是简单粗暴直接把绝对值拆开变成一个正数和一个负数的最大值了,而不是常用的曼哈顿转切比雪夫。
然后我们预处理 \(±x_i±y_i\) 这四个东西的最大值就可以了。

这道题加了 \(w\) 的限制。考虑对 \(w\) 进行从小到大排序,然后分治。
对于每个数 \(i\)

对于每个询问点 \(q\)dfs(l,r) 计算点集区间 \((l,r)\) 内的贡献。我们取 \(mid\)。并 \(O(1)\) 求出 \(mid...n\) 中和 \(q\) 距离最大的一个的距离,记为 \(d\)
为什么分治能维护时间复杂度呢,想象一下原来求一个答案我们要线性时间,现在如果是求答案可以只遍历 \((l,r)\) (通常分治只需要考虑区间内的元素)是不是肉眼可见的复杂度顿时就变低了呢?这一次每个点的询问是 \(O(1)\) 的,也是 \(\log n\) 次就找到了)
上一次我们分治是求出一个区间内所有东西的答案,这次我们是求出一个区间内所有东西的贡献。
(本质是每个元素的答案只需要 \(\log n\) 次就被找到了)

  • 如果 \(w_{mid} < d\),那么 \(k...n\) 对答案的贡献至少\(w_{mid}\)。(因为取到 \(d\) 的点 \(j\),贡献为 \(\min\{w_j,d\}\),这俩东西都是 \(\ge w_{mid}\) 的)那么之前的由于上界是 \(w_{mid}\) 不会对答案产生贡献。可以缩小范围到 \((mid+1,n)\)
  • 如果 \(w_{mid} \ge d\),那么 \(k...n\) 对答案的贡献 \(d\)(因为 \(k...n\) 的所有元素 \(i\) 都满足 \(w_i \ge dis_{i,q}\),那么每个元素的贡献都是 \(dis_{i,q}\))那么我们只需考虑 \(1,mid-1\) 的贡献即可。

代码长度只有 \(1k\),牛逼的分治!!!
不过我写了 \(2k\),跑了 \(300ms\) 左右。不得不说分治真的厉害

#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
struct town {int x, y, w;} to[100010]; 
bool cmp(town a, town b){return a.w < b.w;}
int qx[100010], qy[100010];
int a[100010], b[100010], c[100010], d[100010], ans[100010];
int dis(int mid, int now) {
    return max({-qx[now] - qy[now] + a[mid],
     -qx[now] + qy[now] + b[mid], qx[now] - qy[now] + c[mid], 
     qx[now] + qy[now] + d[mid]});
}
void dfs(int l, int r, int now){
    if(l > r) {return;}
    int mid = (l + r) >> 1; int dx = dis(mid, now); 
    if(to[mid].w < dx) {
        ans[now] = max(ans[now], to[mid].w);
        dfs(mid + 1, r, now);
    }
    else {
        ans[now] = max(ans[now], dx);
        dfs(l, mid - 1, now);
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //think twice,code once.
    //think once,debug forever.
    int t; cin >> t;
    while(t--) {
        int n, q; cin >> n >> q;
        f(i, 1, n) cin >> to[i].x >> to[i].y >> to[i].w; 
        sort(to + 1, to + n + 1, cmp);
        f(i, 1, q) cin >> qx[i] >> qy[i];
        a[n + 1] = b[n + 1] = c[n + 1] = d[n + 1] = -inf;
        for(int i = n; i >= 1; i--) {
            a[i] = max(a[i + 1], to[i].x + to[i].y);
            b[i] = max(b[i + 1], to[i].x - to[i].y);
            c[i] = max(c[i + 1], -to[i].x + to[i].y);
            d[i] = max(d[i + 1], -to[i].x - to[i].y);
        }
        f(i, 1, q) ans[i] = 0;
        f(i, 1, q) dfs(1, n, i);
        f(i, 1, q) cout << ans[i] << endl;
    }
    return 0;
}

upd:听说这个东西叫整体二分。
upd2:我的做法复杂度是对的,可以避免使用 multiset,转而使用优先队列来卡常:
我们再开一个桶数组表示一个元素出现的次数,以判断合法性。
插入元素:桶内该元素++,优先队列里加入该元素。
删除元素:桶内该元素--。
查询最大值:不断弹出元素直到这个元素在桶内不为0.(合法)
复杂度同样是 \(O(n \log n)\),但是二叉堆维护的东西比较少,常数比较小。

曼哈顿距离相关计算

求平面上距离给定点的曼哈顿距离最大的点。这是可以 \(O(n)\) 预处理,\(O(1)\) 查询的东西:

\[\max\{|x_i-x|+|y_i-y|\} \\ =\max\{\max\{x_i-x,x-x_i\}+\max\{y_i-y,y-y_i\}\} \\ =\max\{x_i-x+y_i-y,x-x_i+y_i-y,x-x_i+y_i-y,x_i-x+y-y_i\} \]

思路还是把绝对值换成 \(\max\),但是简单粗暴直接把绝对值拆开变成一个正数和一个负数的最大值了,而不是常用的曼哈顿转切比雪夫。
然后我们预处理 \(±x_i±y_i\) 这四个东西的最大值就可以了。(平面最远点对,只需要比对两对最大值之和即可。)

求平面上给定点的曼哈顿距离最小的点。考虑这时一个最大一个最小没有传递性,只能分开讨论相对位置。例如在左下方的点,式子是:

\[\min\{x - x_i + y - y_i\} = x + y - \max\{x_i + y_i\} \]

可以对 \(x\) 排序之后:

  • 遇到加入点,就对树状数组中 \(y\) 的取值更新为 \(x+y\)
  • 遇到查询,就查询当前 \(0 \sim y\) 的前缀最大值。

排序保证了 \(x \ge x_i\),树状数组保证了 \(y \ge y_i\)

其他三个方向,同理。

CF254D. Rats

【题意】

一个地图为 \(n \times m\) 的平面的仓库里面,有一些老鼠睡着了。主人要把 \(2\) 个炸弹放在仓库里,把所有老鼠炸死。

仓库里面有三种地板:\(\mathtt X\) 表示墙壁,\(\mathtt .\) 表示空地,\(\mathtt R\) 表示存在一只睡着的老鼠的空地。\(2\) 个炸弹只能放在后两种地板中,并且放的位置不能重叠。

每一个炸弹的作用范围是:所有不经过墙壁可以走不超过 \(d\) 格到达炸弹放置点的位置。这些范围内的老鼠会被炸死。

给定地图,求炸弹应该放置在哪两个位置上。

\(4 \le n , m \le 1000, 1 \le d \le 8\),保证有 \(\geq 1\) 只老鼠,保证有 \(\ge 2\) 个空地。

【分析】

首先发现一个结论,到一个点曼哈顿距离 \(\le d\) 的点组成的曼哈顿正方形大小为 \(1 + 4 + 8 + ... + 4d\),大概是 \(O(2d^2)\) 的,当 \(d=8\) 的时候为 \(145\)。又因为炸弹的作用范围小于等于对应的曼哈顿正方形,所以一个炸弹最多炸死附近的 \(145\) 只老鼠。

这个结论带来了什么作用?首先如果老鼠的个数 \(\ge 290\),就可以判断不存在方案。其次,可以初步缩小判断范围。一个炸弹可以炸死一群老鼠,这一群老鼠中间每一对老鼠的曼哈顿距离都 \(\le d\)。那么两个炸弹只能炸死两群老鼠,我们找到老鼠中曼哈顿距离最大的两只,如果曼哈顿距离 \(> 2d\),那么只能是分别在两群老鼠中。对于一个很大的地图,我们只要在这两群老鼠周围找就行了,不用整张地图找。这样复杂度就比较有保障了。

对于曼哈顿距离的最大值,考虑 \(\max \limits_{i, j} dis_{i,j} = \max \limits_{i, j}(\max \{x_i+y_i-x_j-y_j,x_i-y_i-x_j+y_j,-x_i+y_i+x_j-y_j,-x_i-y_i+x_j+y_j\})\),因此记录 \(x_i+y_i, x_i-y_i, -x_i+y_i,-x_i-y_i\) 的最大值,然后只有两对可能的情况即可。时间复杂度 \(O(n)\)

考虑要怎么找。这个范围明确地说应该是这两只距离最远的老鼠往外找 \(d\) 个格子能找到的位置。(炸弹走 \(d\) 格可以找到老鼠,老鼠走 \(d\) 格也可以找到炸弹)范围的大小是 \(O(4d^2)\)。然后枚举哪两个位置放置炸弹,\(O(2d^2)\) 时间判断即可。总时间复杂度 \(O(n+32d^4)\),常数比较大,但是可以通过。

但是这个题有一个坑人的地方就出现了:考虑一个炸弹可以炸掉所有老鼠,然后没有搜到其他炸弹放置点的情况,例如:

XXXX
XRXX
XX.X
XXXX

这样的话需要特判掉,事实上因为如果存在 \(\ge 2\) 只老鼠,那么这时候一定能找到 \(\ge 2\) 个位置放置炸弹,于是我们特判老鼠只有 \(1\) 只的情况即可。

其他做法:也可以先对一只老鼠找能炸到它的位置,再对于这个炸弹没有炸到的老鼠去找能炸到它的位置。采用提前记录每个炸弹能炸到多少只老鼠的方式,时间复杂度可以做到 \(O(8d^4)\)

my solution

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
int n, m, d; 
struct node {
    int x, y;
    int pxpy, pxmy, mxpy, mxmy;
}a[1000010];
char c[1010][1010];
int num[1010][1010];
int cnt;
bool cmp1(node x, node y){
    return x.pxpy>y.pxpy;
}
bool cmp2(node x, node y){
    return x.pxmy>y.pxmy;
}
bool cmp3(node x, node y){
    return x.mxpy>y.mxpy;
}
bool cmp4(node x, node y){
    return x.mxmy>y.mxmy;
}
vector<pii> p;
int dis[1010][1010];
#define fi first
#define se second
int dx[] = {1,-1,0,0};
int dy[]={0,0,1,-1};
void dfs(int x, int y, int nd) {
    queue<pair<pii, int>> q;
    q.push({{x,y},0});
    while(!q.empty()) {
        pair<pii, int>  cur = q.front(); q.pop();
        
        if(dis[cur.fi.fi][cur.fi.se] == inf) {
            p.push_back({cur.fi.fi, cur.fi.se});
        }
        if(dis[cur.fi.fi][cur.fi.se] <= cur.se) continue;
        else if(cur.se >= nd) {
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            continue;
        }
        else {
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            f(i, 0, 3) {
                int nx = cur.fi.fi + dx[i], ny = cur.fi.se + dy[i];
                if(nx < 1 || nx > n || ny < 1 || ny > m || c[nx][ny] == 'X') continue;
                if(dis[nx][ny] >dis[cur.fi.fi][cur.fi.se] + 1) q.push({{nx, ny}, dis[cur.fi.fi][cur.fi.se] + 1});
            }
        }
    }
}
int calc(pii s, pii t) {
    vector<pii> tt;  //fix
    int ans = 0;
    queue<pair<pii, int>> q;
    q.push({s,0});
    while(!q.empty()) {
        pair<pii, int>  cur = q.front(); q.pop();
        if(dis[cur.fi.fi][cur.fi.se] == inf) {
            if(num[cur.fi.fi][cur.fi.se]) {
                ans++;
            }
        }
        if(dis[cur.fi.fi][cur.fi.se] <= cur.se) continue;
        else if(cur.se >= d) {
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            tt.push_back(cur.fi);
            continue;
        }
        else {
            tt.push_back(cur.fi);
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            f(i, 0, 3) {
                int nx = cur.fi.fi + dx[i], ny = cur.fi.se + dy[i];
                if(nx < 1 || nx > n || ny < 1 || ny > m || c[nx][ny] == 'X') continue;
                if(dis[nx][ny] > dis[cur.fi.fi][cur.fi.se] + 1)q.push({{nx, ny}, dis[cur.fi.fi][cur.fi.se] + 1});
            }
        }
    }
    q.push({t,0});
    while(!q.empty()) {
        pair<pii, int>  cur = q.front(); q.pop();
        if(dis[cur.fi.fi][cur.fi.se] == inf) {
            if(num[cur.fi.fi][cur.fi.se]) {
                ans++;
            }
        }
        if(dis[cur.fi.fi][cur.fi.se] <= cur.se) continue;
        else if(cur.se >= d) {
            tt.push_back(cur.fi);
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            continue;
        }
        else {
            tt.push_back(cur.fi);
            dis[cur.fi.fi][cur.fi.se] = cur.se;
            f(i, 0, 3) {
                int nx = cur.fi.fi + dx[i], ny = cur.fi.se + dy[i];
                if(nx < 1 || nx > n || ny < 1 || ny > m || c[nx][ny] == 'X') continue;
                if(dis[nx][ny] >dis[cur.fi.fi][cur.fi.se] + 1)q.push({{nx, ny}, dis[cur.fi.fi][cur.fi.se] + 1});
            }
        }
    }    
    for(pii x : tt ) dis[x.fi][x.se] = inf;
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);    
    time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    cin >> n >> m >> d;
    f(i, 1, n) f(j, 1, m) {
        cin>>c[i][j];
        if(c[i][j] == 'R') {
            ++cnt;
            a[cnt].x = i, a[cnt].y = j;
            a[cnt].pxpy=i+j;
            a[cnt].pxmy=i-j;
            a[cnt].mxpy=-i+j;
            a[cnt].mxmy=-i-j;
        }
    }
    if(cnt >= 300) {
        cout << -1 << endl;
        return 0;
    }
    if(cnt == 1) {
        cout << a[1].x << " " << a[1].y << " ";
        f(i, 1, n) f(j, 1, m) {
            if(c[i][j] == '.') {cout << i << " " << j << endl; return 0;}
        }
    }    
    node mxt[8];
    sort(a+1,a+cnt+1,cmp1);
    mxt[1] = a[1];
    sort(a+1,a+cnt+1,cmp2);
    mxt[2] = a[1];
    sort(a+1,a+cnt+1,cmp3);
    mxt[3] = a[1];
    sort(a+1,a+cnt+1,cmp4);
    mxt[4] = a[1];   
    f(i, 1, cnt) {
        num[a[i].x][a[i].y] = i;
    }
    f(i, 1, n) f(j, 1, m) dis[i][j] = inf;
    if(mxt[1].pxpy + mxt[4].mxmy >= mxt[2].pxmy + mxt[3].mxpy) {
        dfs(mxt[1].x, mxt[1].y, d);
        dfs(mxt[4].x, mxt[4].y, d);
    } 
    else {
        dfs(mxt[2].x, mxt[2].y, d);
        dfs(mxt[3].x, mxt[3].y, d);
    }
    f(i, 1, n) f(j, 1, m) dis[i][j] = inf;
    f(i, 0, (int)p.size()-1) {
        f(j, i + 1, (int)p.size()-1) {
            if(calc(p[i], p[j]) == cnt)  {
                cout << p[i].fi << " " << p[i].se << " " << p[j].fi << " " << p[j].se << endl;
                return 0;
            }
        }
    }
    cout<<-1<<endl;
    time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}

by -XraY, O(8d^4) solution

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <cassert>
#include <ctime>

using namespace std;

#ifdef WIN32
	#define LLD "%I64d"
#else
	#define LLD "%lld"
#endif


typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<bool> vb;
typedef vector<vb> vvb;
typedef vector<ll> vll;
typedef vector<vll> vvll;

#define TASKNAME "text"
#define pb push_back
#define mp make_pair
#define EPS (1E-9)
#define INF ((int)1E9)
#define sqr(x) ((x) * (x))
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#define sz(x) ((int)(x).size())
  
int h, w, di;
const int maxn = 1000;
char s[maxn + 1];
int a[maxn][maxn];

int used[maxn][maxn], maxu;

int d[maxn][maxn];
int st[maxn * maxn][2];

const int go[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
inline bool ok(int y, int x) {
	return y >= 0 && y < h && x >= 0 && x < w && a[y][x] >= 0;
}

int bfs(int y1, int x1) {
	int res = 0;
	if (used[y1][x1] != maxu)
		res += a[y1][x1] == 1;
	
	int r = 0;
	st[r][0] = y1, st[r][1] = x1;
	r++;
	d[y1][x1] = 0;
	used[y1][x1] = maxu;
	
	for (int l = 0; l < r; l++) {
		int y = st[l][0], x = st[l][1];
		if (d[y][x] == di)
			break;
		for (int g = 0; g < 4; g++) {
			int yn = y + go[g][0], xn = x + go[g][1];
			if (!ok(yn, xn))
				continue;
			if (used[yn][xn] == maxu && d[yn][xn] <= d[y][x] + 1)
				continue;
			if (used[yn][xn] != maxu)
				res += a[yn][xn] == 1;
			d[yn][xn] = d[y][x] + 1;
			st[r][0] = yn, st[r][1] = xn, r++;
			used[yn][xn] = maxu;
		}
	}
	
	return res;
}

int rats[maxn * maxn][2];

int main() {     
	#ifdef DEBUG
		freopen(TASKNAME".in", "r", stdin);
		freopen(TASKNAME".out", "w", stdout);
	#else
		freopen("input.txt", "r", stdin);
		freopen("output.txt", "w", stdout);
	#endif
	
	while (scanf("%d%d%d", &h, &w, &di) >= 1) {
		int cntR = 0;
		int yr = -1, xr = -1;
		for (int i = 0; i < h; i++) {
			scanf("%s", s);
			for (int j = 0; j < w; j++) {
				a[i][j] = (s[j] == 'X' ? -1 : s[j] == 'R' ? 1 : 0);
				if (s[j] == 'R') {
					yr = i, xr = j;
					rats[cntR][0] = i, rats[cntR][1] = j;
					cntR++;
				}
			}
		}
		
		assert(yr != -1);
		
		memset(used, 0, sizeof(used));
		maxu = 0;
		
		bool found = 0;
		int yans1, xans1, yans2, xans2;
		for (int dy1 = -di; !found && dy1 <= di; dy1++) {
			int maxdx1 = di - abs(dy1);
			for (int dx1 = -maxdx1; !found && dx1 <= maxdx1; dx1++) {
				int y1 = yr + dy1, x1 = xr + dx1;
				if (!ok(y1, x1))
					continue;
				
				++maxu;
				
				if (bfs(y1, x1) == cntR) {
					yans1 = y1, xans1 = x1;
					yans2 = -1, xans2 = -1;
					found = 1;
					break;
				}
				
				int cy = -1, cx = -1;
				for (int i = 0; i < cntR; i++) {
					int y = rats[i][0], x = rats[i][1];
					if (used[y][x] != maxu) {
						cy = y, cx = x;
						break;
					}
				}
				assert(cy != -1);
				
				for (int dy2 = -di; !found && dy2 <= di; dy2++) {
					int maxdx2 = di - abs(dy2);
					for (int dx2 = -maxdx2; dx2 <= maxdx2; dx2++) {
						int y2 = cy + dy2, x2 = cx + dx2;
						if (!ok(y2, x2))
							continue;
						
						++maxu;
						if (bfs(y1, x1) + bfs(y2, x2) == cntR) {
							found = 1;
							yans1 = y1, xans1 = x1;
							yans2 = y2, xans2 = x2;
							break;
						}
					}
				}
			}
		}
		
		if (!found)
			printf("-1\n");
		else {
			if (yans2 == -1) {
				for (int i = 0; i < h; i++)
					for (int j = 0; j < w; j++) {
						if ((i != yans1 || j != xans1) && a[i][j] >= 0)
							yans2 = i, xans2 = j;
					}
				assert(yans2 != -1);
			}
			printf("%d %d %d %d\n", yans1 + 1, xans1 + 1, yans2 + 1, xans2 + 1);
		}
// 		break;
	}	
	return 0;
}

by equation314, very short code.

这份代码巧妙地将 vector 作为参数传进函数,非常值得学习。bfs 函数里他做的比我好太多了。他的 vis 记录的是每轮不重样的 tot,避免了讨论的麻烦。开两个平行的数组一个 q 记录节点本身,一个 e 记录距离,避免写太多 pair。bfs 写的好了,码量减少多了。我的两个函数 dfs 和 calc 做的是同一件事,应该放在一个函数里。

#include <bits/stdc++.h>
#define pr pair<int,int>
#define X first
#define Y second
using namespace std;

const int d[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int N,M,S,D,b[1005][1005],c[1005][1005],e[305],tot;
char a[1005][1005];
vector<pr> p,q1,q2,q3;

void init()
{
	scanf("%d%d%d",&N,&M,&D),S=D*(D+1)*4+2;
	for (int i=1; i<=N; i++) scanf("%s",a[i]+1);
}

void bfs(int sx,int sy,vector<pr>&q)
{
	q.clear(),b[sx][sy]=++tot,q.push_back(pr(sx,sy));
	for (int l=0,r=0; l<=r&&e[l]<D; l++)
		for (int x=q[l].X,y=q[l].Y,i=0; i<4; i++)
		{
			int xx=x+d[i][0],yy=y+d[i][1];
			if (a[xx][yy]!='X'&&b[xx][yy]!=tot) e[++r]=e[l]+1,b[xx][yy]=tot,q.push_back(pr(xx,yy));
		}
}

void doit()
{
	for (int i=1; i<=N; i++)
		for (int j=1; j<=M; j++) if (a[i][j]=='R') p.push_back(pr(i,j));
	if (p.size()>S) {puts("-1"); return;}
	bfs(p[0].X,p[0].Y,q1);
	for (auto i:q1)
	{
		int x1,y1,x2,y2,t; pr o(0,0);
		bfs(x1=i.X,y1=i.Y,q2);
		for (auto j:q2) c[j.X][j.Y]=t=tot;
		for (auto j:p) if (b[j.X][j.Y]!=tot) o=j;
		if (!o.X)
		{	
			for (int j=1; j<=N; j++)
				for (int k=1; k<=M; k++) if (a[j][k]!='X'&&j!=x1&&k!=y1) {printf("%d %d %d %d\n",x1,y1,j,k); return;}
		}
		bfs(o.X,o.Y,q2);
		for (auto j:q2)
		{
			bfs(x2=j.X,y2=j.Y,q3);
			bool ok=1;
			for (auto k:p) if (b[k.X][k.Y]!=tot&&c[k.X][k.Y]!=t) {ok=0; break;}
			if (ok) {printf("%d %d %d %d\n",x1,y1,x2,y2); return;}
		}
	}
	puts("-1");
}

int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	init();
	doit();
	return 0;
}
posted @ 2022-07-11 10:07  OIer某罗  阅读(159)  评论(0编辑  收藏  举报