Codeforces 821D
题意:题意:$n \times m$的地图,有$k$个位置是点亮的,有$4$个移动方向,每次可以移动到相邻的点亮位置,每次站在初始被点亮某个位置,暂时使某行或该某列全部点亮,花费为$1$,下一次使用时,上一次暂时点亮被熄灭.
问从$(1,1)$到$(n,m)$的最小花费
这题发现可以直接跑最短路。。每次循环找下一个灯如果相邻费用为0,如果能点亮一列过去费用为1,否则为INF,复杂度有点神奇
可以将行列缩成一个点,因为题目中点亮一次是一行或一列。然后每个灯可以连到周围能到的行列和灯,相应的边权看着办,然后边权只有01,跑0-1BFS。
2018.10.31 upd: 0-1 BFS 不要开vis数组!用dis松弛即可。
知识点
1、点拆了数组要开够大小
2、不要随便叉自己的想法!
3、map.find()
用法
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<climits>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const int MAXN = 10000 + 5, INF = 1000000000;
struct lt {int r, c;} li[MAXN];
struct edge {int u, v, w, nxt; } ed[10000005];
int n, m, k, en, hd[MAXN * 3], dis[MAXN * 3];
map<pair<int, int>, int > ma;
deque<int > q;
void ins(int x, int y, int w) {ed[++en] = (edge){x, y, w, hd[x]}, hd[x] = en;}
void clean() {
en = 0, ms(hd, -1);
}
int solve() {
scanf("%d%d%d", &n, &m, &k);
clean();
for (int i = 1; i <= k; ++i) scanf("%d%d", &li[i].r, &li[i].c), ma[make_pair(li[i].r, li[i].c)] = i;
for (int i = 1; i <= k; ++i) {
int x = li[i].r, y = li[i].c;
ins(i, k + x, 1), ins(k + x, i, 0);
if (x - 1 > 0) ins(i, k + x - 1, 1), ins(k + x - 1, i, 0);
if (x + 1 <= n) ins(i, k + x + 1, 1), ins(k + x + 1, i, 0);
ins(i, k + n + y, 1), ins(k + n + y, i, 0);
if (y - 1 > 0) ins(i, k + n + y - 1, 1), ins(k + n + y - 1, i, 0);
if (y + 1 <= m) ins(i, k + n + y + 1, 1), ins(k + n + y + 1, i, 0);
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
for (int j = 0; j < 4; ++j) {
int tx = dx[j] + x, ty = dy[j] + y;
if (tx > 0 && ty > 0 && tx <= n && ty <= m) {
map<pair<int, int >, int >::iterator it = ma.find(make_pair(tx, ty));
if (it == ma.end()) continue ;
ins(i, it->second, 0), ins(it->second, i, 0);
}
}
}
for (int i = 0; i <= n + m + k; ++i) dis[i] = INF;
dis[1] = 0, q.push_back(1);
while (!q.empty()) {
int u = q.front(); q.pop_front();
for (int i = hd[u]; i > 0; i = ed[i].nxt) {
edge &e = ed[i];
if (dis[e.v] > dis[u] + e.w) {
dis[e.v] = dis[u] + e.w;
if (e.w == 0) q.push_front(e.v); else q.push_back(e.v);
}
}
}
int ans = min(dis[k + n], dis[k + n + m]);
for (int i = 1; i <= k; ++i) if (li[i].r == n && li[i].c == m) ans = min(ans, dis[i]);
if (ans == INF) cout << -1; else cout << ans;
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}