codeforces div2 677 D
http://codeforces.com/problemset/problem/677/D
题目大意:
给你一个n*m的图,上面有p种钥匙(p<=n*m),每种钥匙至少有一个,期初所有为1的钥匙都是可拿的,拿到了该钥匙以后(假设该钥匙的val是v)就可以拿v+1种类的钥匙。问最后拿到第p个钥匙最少走多少步?(钥匙为p种类的只有一种)
思路:官方题解貌似是二维线段树的,不过这题可以用另外一个方法来优化。
首先假设我们目前在的颜色的c,我们要到c+1颜色的格子里面去,如果单纯的对于每种颜色c跑一次spfa的话,那么就是n*n*m*m了,所以我们分类讨论。当len(c)*len(c+1)<=n*m的时候,直接暴力就好了,反之就跑spfa。因为len(c)*len(c+1)的组数必然不多,所以肯定比每次直接跑n*m的spfa要好。
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second const int inf = 0x3f3f3f3f; const int maxn = 300 + 5; int n, m, p; int atlas[maxn][maxn]; vector<pair<int, int> > v[maxn * maxn]; int dp[maxn][maxn], dis[maxn][maxn]; bool vis[maxn][maxn]; int dx[] = {-1, 0, 1, 0}; int dy[] = {0, 1, 0, -1}; int solve(){ for (int i = 1; i < p; i++){ int len1 = v[i].size(); int len2 = v[i + 1].size(); if (len1 * len2 <= n * m){ for (int j = 0; j < len1; j++){ int jx = v[i][j].fi, jy = v[i][j].se; for (int k = 0; k < len2; k++){ int kx = v[i + 1][k].fi, ky = v[i + 1][k].se; dp[kx][ky] = min(dp[kx][ky], dp[jx][jy] + abs(kx - jx) + abs(ky - jy)); } } } else { memset(dis, -1, sizeof(dis)); memset(vis, false, sizeof(vis)); queue<pair<int, int> > que; for (int j = 0; j < len1; j++){ int jx = v[i][j].fi, jy = v[i][j].se; dis[jx][jy] = dp[jx][jy]; que.push(mk(jx, jy)); vis[jx][jy] = true; } while (!que.empty()){ pair<int, int> u = que.front(); que.pop(); vis[u.fi][u.se] = false; for (int j = 0; j < 4; j++){ int jx = u.fi + dx[j], jy = u.se + dy[j]; if (jx <= 0 || jx > n || jy <= 0 || jy > m) continue; if (dis[jx][jy] == -1 || dis[jx][jy] > dis[u.fi][u.se] + 1){ dis[jx][jy] = dis[u.fi][u.se] + 1; if (!vis[jx][jy]){ vis[jx][jy] = true; que.push(mk(jx, jy)); } } } } for (int j = 0; j < len2; j++){ int jx = v[i + 1][j].fi, jy = v[i + 1][j].se; dp[jx][jy] = min(dp[jx][jy], dis[jx][jy]); } } } int px = v[p][0].fi, py = v[p][0].se; return dp[px][py]; } int main(){ cin >> n >> m >> p; for (int i = 1; i <= n; i++){ for (int j = 1; j <= m; j++){ int c; scanf("%d", &c); if (c == 1) dp[i][j] = i + j - 2; else dp[i][j] = inf; v[c].pb(mk(i, j)); atlas[i][j] = c; } } printf("%d\n", solve()); return 0; }