bzoj2143 飞飞侠

2143: 飞飞侠

Time Limit: 50 Sec  Memory Limit: 259 MB
Submit: 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

Sample Output

Z
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算法的理解,挺不错的啊.
   附上另外几道题的链接:bzoj41522017计蒜之道复赛百度地图导航.
#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;
}

 

posted @ 2018-02-13 12:33  zbtrs  阅读(305)  评论(0编辑  收藏  举报