2143: 飞飞侠
Time Limit: 50 Sec Memory Limit: 259 MBSubmit: 1158 Solved: 399
[Submit][Status][Discuss]
Description
飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。如下图 (从红色街区交费以后可以跳到周围的任意蓝色街区。) 现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。
Input
输入的第一行包含两个整数N和M,分别表示行数和列数。接下来是2个N×M的自然数矩阵,为Bij和Aij 最后一行六个数,分别代表X,Y,Z所在地的行号和列号。
Output
第一行输出一个字符X、Y或者Z。表示最优集合地点。第二行输出一个整数,表示最小费用。如果无法集合,只输出一行NO
Sample Input
4 4
0 0 0 0
1 2 2 0
0 2 2 1
0 0 0 0
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
2 1 3 4 2 2
0 0 0 0
1 2 2 0
0 2 2 1
0 0 0 0
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
2 1 3 4 2 2
Sample Output
Z
15
【范围】
100% 1 < = N, M < = 150; 0 < = Aij < = 10^9; 0 < = Bij < = 1000
15
【范围】
100% 1 < = N, M < = 150; 0 < = Aij < = 10^9; 0 < = Bij < = 1000
分析:非常好的一道题!
很容易能看出这道题其实考的就是最短路,但是边数太多了,直接上会T掉.这是最短路中的一类问题,如何把连的边变少?
我做过的这类题中有设置一个入点和出点的,然后把边通过这两个点相连,也有对输入做某些处理,得出性质,知道哪些边是无用的,删去.这道题要用到将边等价的思想.
可以把弹射距离看作能量,如果能量为0则必须原地不动充能,花费为a[i][j],如果有能量,则向相邻方向移动一格不需要花费,能量-1.这样整个图就变成了每个点向四周的点连边.
维护的图其实是一个分层图,设f[i][j][k]表示走到点(i,j)处,还有k点能量的最小花费.vis数组也要记录三维的vis[i][j][k].如果直接跑spfa会超时,需要用dijkstra.如果直接跑dijkstra还是会超时.怎么优化呢?如果已经走到了X,Y,Z三个点,并且X,Y,Z三个点的答案不会再被更新了,那么就可以返回了.怎么维护?
在向4周走的操作上再加一个操作:原地不动,并不充能.这样所有的答案都会汇集到vis[i][j][0]上,只要判断X,Y,Z的vis[0]是不是都被访问过即可.但这能保证XYZ不会继续被更新吗?根据dijkstra算法的性质,我们用小根堆维护,每次移动花费最小的点,既然已经更新到vis[0]了,那么以后所有的花费肯定都比当前大,故不可能更新了.
最后统计答案的时候注意不要弄混了.每次选择dijkstra选定的是起点,统计的是终点.
这道题加深了我对最短路如何少连边和dijkstra算法的理解,挺不错的啊.
附上另外几道题的链接:bzoj4152,2017计蒜之道复赛百度地图导航.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int dx[] = {0,0,1,-1,0},dy[] = {1,-1,0,0,0}; const ll inf = 10000000000000000; const int maxn = 151; int n,m; ll cost[maxn][maxn],b[maxn][maxn],X[4],Y[4],f[maxn][maxn][maxn * 2]; bool vis[maxn][maxn][maxn * 2]; ll a1,a2,b1,b2,c1,c2,ans; struct node { ll x,y,nowcost,lastenergy; bool operator < (const node &a) const { return nowcost > a.nowcost; } }; void dijkstra(int pos) { for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = 0; k <= n + m; k++) { vis[i][j][k] = 0; f[i][j][k] = inf; } priority_queue<node> q; node temp; temp.x = X[pos]; temp.y = Y[pos]; temp.nowcost = cost[X[pos]][Y[pos]]; temp.lastenergy = b[X[pos]][Y[pos]]; q.push(temp); vis[X[pos]][Y[pos]][0] = 1; f[X[pos]][Y[pos]][temp.lastenergy] = cost[X[pos]][Y[pos]]; while (!q.empty()) { if (vis[X[1]][Y[1]][0] && vis[X[2]][Y[2]][0] && vis[X[3]][Y[3]][0]) return; node u = q.top(); q.pop(); ll x = u.x,y = u.y,k = u.lastenergy; if (vis[x][y][k]) continue; vis[x][y][k] = 1; if (u.lastenergy) { for (int i = 0; i <= 4; i++) { ll nx = x + dx[i],ny = y + dy[i]; if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) { if (f[nx][ny][k - 1] > f[x][y][k]) { f[nx][ny][k - 1] = f[x][y][k]; node temp; temp.x = nx; temp.y = ny; temp.lastenergy = k - 1; temp.nowcost = f[nx][ny][k - 1]; q.push(temp); } } } } else { if (f[x][y][0] + cost[x][y] < f[x][y][b[x][y]]) { f[x][y][b[x][y]] = f[x][y][0] + cost[x][y]; node temp; temp.x = x; temp.y = y; temp.lastenergy = b[x][y]; temp.nowcost = f[x][y][b[x][y]]; q.push(temp); } } } } int main() { scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { scanf("%lld",&b[i][j]); b[i][j] = min(b[i][j],1LL * (max(i - 1,n - i) + max(j - 1,m - j))); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%lld",&cost[i][j]); for (int i = 1; i <= 3; i++) scanf("%lld%lld",&X[i],&Y[i]); dijkstra(1); a1 = f[X[2]][Y[2]][0],a2 = f[X[3]][Y[3]][0]; dijkstra(2); b1 = f[X[1]][Y[1]][0],b2 = f[X[3]][Y[3]][0]; dijkstra(3); c1 = f[X[1]][Y[1]][0],c2 = f[X[2]][Y[2]][0]; ans = inf; char anss; if (b1 + c1 < ans) { ans = b1 + c1; anss = 'X'; } if (a1 + c2 < ans) { ans = a1 + c2; anss = 'Y'; } if (a2 + b2 < ans) { ans = a2 + b2; anss = 'Z'; } if (ans >= inf) printf("NO"); else cout << anss << endl << ans << endl; return 0; }