[CTSC1999]营救大兵瑞恩

Description
1944年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但是幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其在南北方向被划分为 N 行,在东西方向被划分为M列,于是整个迷宫被划分为 NM 个单元。我们用一个有序数对(单元的行号,单元的列号)来表示单元位置。南北或东西方向相邻的两个单元之间可以互通,或者存在一扇锁着的门,又或者存在一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分为 P 类,打开同一类的门的钥匙相同,打开不同类的门的钥匙不同。大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角,也就是说,麦克可以直接进入 (1,1) 单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间忽略不计。你的任务是帮助麦克以最快的方式抵达瑞恩所在单元,营救大兵瑞恩。

Input
第一行是三个整数,依次表示 N,M,P 的值;
第二行是一个整数 K ,表示迷宫中门和墙的总个数;
I+2行( 1<=I<=K ),有5个整数,依次为 Xi1,Yi1,Xi2,Yi2,Gi
Gi>=1 时,表示 (Xi1,Yi1) 单元与 (Xi2,Yi2) 单元之间有一扇第 Gi 类的门,
Gi=0 时,表示 (Xi1,Yi1) 单元与 (Xi2,Yi2) 单元之间有一堵不可逾越的墙;
(其中, |Xi1Xi2|+|Yi1Yi2|=1 0<=Gi<=P
K+3 行是一个整数 S ,表示迷宫中存放的钥匙总数;
K+3+J行( 1<=J<=S ),有3个整数,依次为 Xi,Yi,Qi
表示第J把钥匙存放在 (Xi,Yi) 单元里,并且第 J 把钥匙是用来开启第Qi类门的。(其中 1<=Qi<=P
注意:输入数据中同一行各相邻整数之间用一个空格分隔。
3<=N,M<=15 ;
1<=P<=10 ;

Output
输出文件只包含一个整数 T ,表示麦克营救到大兵瑞恩的最短时间的值,若不存在可行的营救方案则输出-1。

Sample Input
4 4 9
9
1 2 1 3 2
1 2 2 2 0
2 1 2 2 0
2 1 3 1 0
2 3 3 3 0
2 4 3 4 1
3 2 3 3 0
3 3 4 3 0
4 3 4 4 0
2
2 1 2
4 2 1

Sample Output
14

HINT

思路
disti,j,k表示在 (i,j) 这个点,身上钥匙的状态为 k ,所花费的最小时间,显然(i,j,k)可以向四周没有墙和门的地方连一条权值为1的边,如果 k 包含了一个门的状态,那么这个门也可以打开。到了一个有钥匙的地方,那么这个地方没有这种钥匙的点可以向对应的有这种钥匙的点连一条权值为0的边。最终枚举终点的状态,dist的最小值就是答案。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn=15;
const int maxp=10;
const int inf=0x3f3f3f3f;
const int dx[]= {1,0,-1,0};
const int dy[]= {0,1,0,-1};

struct data
{
    int x,y,s;
};

int kind[maxn+1][maxn+1][4],n,m,p,k,s,tot,ans=inf;
int pre[maxn*maxn*(1<<maxp)+10],now[maxn+1][maxn+1][1<<maxp];
data son[maxn*maxn*(1<<maxp)+10];
int val[maxn*maxn*(1<<maxp)+10],b[maxn+1][maxn+1][1<<maxp];
int dist[maxn+1][maxn+1][1<<maxp],head,tail;
data q[maxn*maxn*(1<<maxp)*4+10];

int check_position(int a,int b,int c)
{
    return (a==dx[c])&&(b==dy[c]);
}

int in_range(int a,int b)
{
    return (a>0)&&(a<=n)&&(b>0)&&(b<=m);
}

int makewall(int a,int b,int c,int d,int k)
{
    for(int i=0; i<4; i++)
    {
        if(check_position(c-a,d-b,i))
        {
            if(!k)
            {
                kind[a][b][i]=-1;
            }
            else
            {
                kind[a][b][i]=k;
            }
        }
    }
    return 0;
}

int ins(int ax,int ay,int as,int bx,int by,int bs,int c)
{
    tot++;
    pre[tot]=now[ax][ay][as];
    now[ax][ay][as]=tot;
    son[tot].x=bx;
    son[tot].y=by;
    son[tot].s=bs;
    val[tot]=c;
    return 0;
}

int spfa(int sx,int sy,int ss)
{
    memset(dist,63,sizeof dist);
    head=0;
    tail=1;
    q[1].x=sx;
    q[1].y=sy;
    q[1].s=ss;
    b[sx][sy][ss]=1;
    dist[sx][sy][ss]=0;
    while(head!=tail)
    {
        head++;
        int ux=q[head].x,uy=q[head].y,us=q[head].s;
        int j=now[ux][uy][us];
        while(j)
        {
            int vx=son[j].x,vy=son[j].y,vs=son[j].s;
            if(dist[vx][vy][vs]>dist[ux][uy][us]+val[j])
            {
                dist[vx][vy][vs]=dist[ux][uy][us]+val[j];
                if(!b[vx][vy][vs])
                {
                    b[vx][vy][vs]=1;
                    tail++;
                    q[tail].x=vx;
                    q[tail].y=vy;
                    q[tail].s=vs;
                }
            }
            j=pre[j];
        }
        b[ux][uy][us]=0;
    }
    return 0;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&k);
    for(int i=1; i<=k; i++)
    {
        int a,b,c,d,e;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
        makewall(a,b,c,d,e);
        makewall(c,d,a,b,e);
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            for(int k=0; k<4; k++)
            {
                if(in_range(i+dx[k],j+dy[k]))
                {
                    if(kind[i][j][k]==0)
                    {
                        for(int l=0; l<1<<p; l++)
                        {
                            ins(i,j,l,i+dx[k],j+dy[k],l,1);
                        }
                    }
                    if(kind[i][j][k]>0)
                    {
                        for(int l=0; l<1<<p; l++)
                        {
                            if((l|(1<<(kind[i][j][k]-1)))==l)
                            {
                                ins(i,j,l,i+dx[k],j+dy[k],l,1);
                            }
                        }
                    }
                }
            }
        }
    }
    scanf("%d",&s);
    for(int i=1; i<=s; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        for(int l=0; l<1<<p; l++)
        {
            if((l|(1<<(c-1)))!=l)
            {
                ins(a,b,l,a,b,l|(1<<(c-1)),0);
            }
        }
    }
    spfa(1,1,0);
    for(int i=0; i<1<<p; i++)
    {
        ans=std::min(ans,dist[n][m][i]);
    }
    if(ans==inf)
    {
        puts("-1");
        return 0;
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值