[NOIP模拟][bzoj 2143][最短路]飞飞侠

题目描述:
题目链接: bzoj 2143 飞飞侠
飞飞国是一个传说中的国度,国家的居民叫做飞飞侠。飞飞国是一个N×M的矩形方阵,每个格子代表一个街区。然而飞飞国是没有交通工具的。飞飞侠完全靠地面的弹射装置来移动。每个街区都装有弹射装置。使用弹射装置是需要支付一定费用的。而且每个弹射装置都有自己的弹射能力。我们设第i行第j列的弹射装置有Aij的费用和Bij的弹射能力。并规定有相邻边的格子间距离是1。那么,任何飞飞侠都只需要在(i,j)支付Aij的费用就可以任意选择弹到距离不超过Bij的位置了。如下图 (从红色街区交费以后可以跳到周围的任意蓝色街区。) 现在的问题很简单。有三个飞飞侠,分别叫做X,Y,Z。现在它们决定聚在一起玩,于是想往其中一人的位置集合。告诉你3个飞飞侠的坐标,求往哪里集合大家需要花的费用总和最低。
Input
输入的第一行包含两个整数N和M,分别表示行数和列数。接下来是2个N×M的自然数矩阵,为Aij和Bij 最后一行六个数,分别代表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
题目分析:
朴素的想法是根据题目要求,如果一个点u可以弹射到另一个点v,就建一条单向边,u指向v,边权为弹射费用,然后作最短路。这种做法建的边的数量极大,甚至存不下,期望得分50分。优化: 直接根据原图特点作最短路,可以多的一些分。然后什么n棵线段树或者bitset优化建边什么的可以过,orz。
正解: 我们引入一个云端的概念。我们定义初始图的高度为0,对于每个点根据它的弹射能力h,在高度为h上建一个对应点,边权为弹射费用。对于云端h的一个点,建5条边,连向云端h-1上的5个点(四个方向上的点,加自己在云端h-1的对应点),边权为0。也就是说当你把一个点弹射到它对应的最大高度云端时,往下走是不消耗费用的。这就体现了你在某个点弹一次(体现在弹射到对应的最大高度云端点)就可以到达一些点(体现在云端上的点只能向下走)。这样做的好处是建的边不会太多,点数和边数同级,为O(N*M*H)(图中最大弹射能力)。再做三遍dijkstra+堆优化,就可以了。最坏时间复杂度O( N3logN ),空间复杂度O( N3 )。
PS:
1、实际操作上不用建边,dijkstra时根据特点走就行。
2、考试数据要比bzoj上的数据强一些儿,所以那些什么直接原图dijkstra在bzoj能过的,是不能满分的。
题目分析:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const long long INF=1e18;
const int fx[5]={0,1,0,-1,0};
const int fy[5]={0,0,1,0,-1};
int n,m,H,A[210][210],B[210][210];
long long dis[210][210][420],w[5],ans,len[5][5];
bool flag1,flag2;
char pe;
struct node{
    int x,y,h;
    friend inline bool operator < (const node &a,const node &b)
    {
        return a.h5];
priority_queuelong long,node> >q;

int readint()
{
    char ch;int i=0,f=1;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') {ch=getchar();f=-1;}
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}

void dijkstra(int sta,int des1,int des2)
{
    flag1=false;flag2=false;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            for(int k=0;k<=H;k++)
                dis[i][j][k]=INF;
    while(!q.empty()) q.pop();
    int x=src[sta].x;int y=src[sta].y;
    dis[x][y][A[x][y]]=B[x][y];
    q.push(make_pair(-B[x][y],src[sta]));
    while(!q.empty())
    {
        node a=q.top().second;
        long long d=dis[a.x][a.y][a.h];
        q.pop();
        if(a.x==src[des1].x&&a.y==src[des1].y&&a.h==0) {flag1=true;len[sta][des1]=d;}//如果出栈了目标点说明就是最短路,一定要判高度为0,因为云端上的更新不一定最优,但到地面相互比较过,就是最优的
        if(a.x==src[des2].x&&a.y==src[des2].y&&a.h==0) {flag2=true;len[sta][des2]=d;}
        if(flag1==true&&flag2==true) return;//两个都找到就不用继续了
        if(a.h>0)
        {
            for(int i=0;i<=4;i++)
            {
                node b;
                b.x=a.x+fx[i];
                b.y=a.y+fy[i];
                b.h=a.h-1;
                if(b.x>=1&&b.x<=n&&b.y>=1&&b.y<=m&&dis[b.x][b.y][b.h]>d)
                {
                    dis[b.x][b.y][b.h]=d;
                    q.push(make_pair(-d,b));
                }
            }
        }
        else//高度为0就再从当前点弹上去
        {
            a.h=A[a.x][a.y];
            if(dis[a.x][a.y][a.h]>d+B[a.x][a.y])
            {
                dis[a.x][a.y][a.h]=d+B[a.x][a.y];
                q.push(make_pair(-dis[a.x][a.y][a.h],a));
            }
        }
    }
}

int main()
{
    //freopen("friend.in","r",stdin);
    //freopen("friend3.out","w",stdout);

    int x,y;
    n=readint();m=readint();
    H=n+m-2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            A[i][j]=min(readint(),H);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            B[i][j]=readint();
    for(int i=1;i<=3;i++)
    {
        src[i].x=readint();
        src[i].y=readint();
        src[i].h=A[src[i].x][src[i].y];
    }
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            len[i][j]=INF;
    dijkstra(1,2,3);w[2]+=len[1][2];w[3]+=len[1][3];
    dijkstra(2,1,3);w[1]+=len[2][1];w[3]+=len[2][3];
    dijkstra(3,2,1);w[2]+=len[3][2];w[1]+=len[3][1];
    ans=INF;
    if(w[1]1];pe='X';}
    if(w[2]2];pe='Y';}
    if(w[3]3];pe='Z';}
    if(ans==INF) printf("NO");
    else printf("%c\n%lld",pe,ans);

    return 0;
}

你可能感兴趣的:(图论问题,NOIP题)