题目链接: Okabe and City
题目大意
n*m的地图, 有k个位置有路灯, (1, 1)位置保证一定有路灯, 人一开始的时候在(1, 1), 要走到(n, m), 并且人只能在有路灯的位置上走, 但可以花费一元钱让某一行或某一列亮起来, 注意在某个时刻只能有一行或一列被点亮, 也就是说, 你点亮一行或一列的时候, 上一次被点亮的那一行或一列就会熄灭, 所以人必须要站在一开始就被点亮的格子才能点亮其他行列, 问走到终点最小花费是多少, 不能走到终点输出-1
思路
最短
建图方法:
1. 相邻的有路灯的格子连边, cost = 0
2. 每一行每一列视作一个顶点, 有路灯格子与其相邻三行三列连边, cost = 1(从点亮的格子, 花费一元将附近某行某列点亮并走到那一行或列)
3. 有路灯格子相邻三行三列与这个格子连边, cost = 0(从点亮的行或列走到原先就有路灯的格子)
4. 如果终点(n, m)最初没有路灯, 将终点所在行与列与终点连边, cost = 0
然后用优先队列优化的Dijkstra
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1E4 + 10, INF = 0x3f3f3f3f;
typedef pair<int, int> P;
struct edge
{
int to, cost;
edge(int a=0, int b=0) :to(a), cost(b) {}
};
vector<edge> G[MAXN*4];
int d[MAXN*4], dx[] = {0, 0, -1, 1, }, dy[] = {1, -1, 0, 0, }, n, m, k, r, c;
map<P, int> mp;
void dij(int s)
{
priority_queue<P, vector<P>, greater<P>> que;
fill(d, d+MAXN*4, INF);
d[s] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top(); que.pop();
int v = p.second;
if(d[v] < p.first) continue;
for(int i=0; i<(int)G[v].size(); ++i)
{
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost)
{
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
for(int i=0; i<k; ++i)
{
scanf("%d%d", &r, &c);
for(int j=-1; j<=1; ++j)
{
G[i].push_back(edge(r+MAXN+j, 1));
G[i].push_back(edge(c+2*MAXN+j, 1));
G[r+MAXN+j].push_back(edge(i, 0));
G[c+2*MAXN+j].push_back(edge(i, 0));
mp[P(r, c)] = i;
}
}
int x, y, nx, ny;
for(auto &ite : mp)
{
x = ite.first.first; y = ite.first.second;
for(int i=0; i<4; ++i)
{
nx = x + dx[i];
ny = y + dy[i];
if(mp.find(P(nx, ny)) != mp.end())
G[mp[P(x, y)]].push_back(edge(mp[P(nx, ny)], 0));
}
}
if(mp.find(P(n, m)) == mp.end())
{
G[n+MAXN].push_back(edge(k, 0));
G[m+2*MAXN].push_back(edge(k, 0));
mp[P(n, m)] = k;
}
dij(mp[P(1, 1)]);
cout << (d[mp[P(n, m)]] == INF ? -1 : d[mp[P(n, m)]]) << endl;
return 0;
}