【基础练习】【最短路堆优dij】tyvj1376 魔域之战题解

P1376 魔域之战
时间: 1000ms / 空间: 131072KiB / Java类名: Main

描述

    小A成功地在紧要关头逃离了神奇山洞,同时他也感觉自己rp大增。现在他站在了一座阴森森的城堡前,这就是江湖人称“死亡城堡”的魔域。为了rp,小A毅然决然地走了进去……
    不愧是死亡城堡,险境丛生,小A又是一个大意的人,XXX他掉进了一个陷阱。
    这是一个n*n的矩阵陷阱,矩阵的每一个小格内都有一个魔鬼,并且这些魔鬼属于不同的种类,种类数不超过p(1<=p<=n*n,有可能存在某一种魔鬼不在矩阵中出现)。每一种魔鬼i都有一种战斗力c[i],不同的魔鬼战斗力不同。打魔鬼也不是那么好玩的,需要消耗小A相应数量的战斗力。当小A打败了一个魔鬼后,此种类型的魔鬼就会全部消失,这样小A就可以自由的在这种类型的格子间传送,不会消耗任何的战斗力。每到一个格子,小A可以向紧邻的上下左右四个格子进发去打魔鬼,直到走出矩阵。小A开始郁闷了,他怎样才能从矩阵的第一行出发,顺利的走到矩阵的最后一行呢?所谓的顺利,就是使自己的战斗力大于0。
    现在小A求助于聪明的你,希望你能求出小A顺利走出陷阱时剩余的最大战斗力。

输入格式

    第一行:两个整数n,p;
    第2到n+1行是一个n*n的矩阵,矩阵中不同的数字代表不同的格子类型;
    第n+2行是p个数,代表p种魔鬼的战斗力;
    最后一行:小A的初始战斗力值W。

输出格式

    一个整数表示小A顺利走出陷阱时剩余的最大战斗力,如果小A走不出陷阱就输出'Dangerous!';

测试样例1

输入

样例一: 
3 3 
1 2 1 
2 1 2 
1 1 1 
1 2 5 

样例二: 
3 3 
1 2 1 
2 1 2 
3 3 3 
1 2 5 
2

输出

样例一: 

样例二: 
Dangerous!

备注

对于30%的数据:1<=n<=50
对于100%的数据:1<=n<=500;w<=maxlongint
看到这个题目就想到图论,当时忍不住感叹这真是个奇怪的想法,然而还是写了···事实证明思路是正确的 这题目可以跑BFS或最短路都可 

BFS的思路,就是上下左右搜索,以点权为代价,搜到一个新的类型,就把相同元素全部入队,同时记录当前剩余体力,如果走到最后一行输出即可

而图论的思路,我想的略麻烦:建立一个原点,向第一排各点连边,边权为代价;每个点向右向下连双向边;相同类型的点连边权为0的双向边,跑堆优dij

这样子边太多了,大数据会爆空间。实际上更好的方法是把同一类型的点当做一个点,每次读入数据记录不同类型点之间的关系,原点连第一排有的点的种类,最后一排出现的种类连向终点,跑SPFA即可

我只写了麻烦版本的代码,然而竟然能过···顺带吐槽下我们信手改题的07级学长,泥萌现在大概大学毕业两年了吧···

//trap
//copyright by ametake
#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
         #include 
        
          using namespace std; const int maxn=250000+10; const int maxm=6001000+10; const int oo=2147483647; struct data { int v,w,next; }e[maxm];//edge 终点,边权,下一条同源边 struct w { int dist,num,k; bool operator < (w n2)const { return dist>n2.dist; } }node[maxn];//每个node是一个点,有编号和dist vector 
         
           vec[maxn]; int ne,m,p;//矩阵大小m*m 边数带计算 int head[maxn],cost[maxn],cnt[maxn]; bool vis[maxn]; int tot,ans; void insert(int x,int y,int w) { ne++; e[ne].v=y; e[ne].w=w; e[ne].next=head[x]; head[x]=ne; } void init() { scanf("%d%d",&m,&p);//读入矩阵长度,鬼种类 memset(head,-1,sizeof(head)); int x; for (int i=1;i<=m;i++) { for (int j=1;j<=m;j++) { scanf("%d",&x); node[(i-1)*m+j].k=x; cnt[x]++; vec[x].push_back((i-1)*m+j); } } for (int i=1;i<=p;i++) scanf("%d",&cost[i]); scanf("%d",&tot); return; } void makeit() { for (int i=1;i<=m;i++) insert(0,i,cost[node[i].k]); for (int i=1;i<=p;i++) { if (cnt[i]>0) { for (int j=0;j 
          
            q; node[k].dist=0; q.push(node[k]); int disj,minj;//dis of j and number of j while (!q.empty()) { minj=q.top().num; disj=q.top().dist; vis[minj]=true; q.pop(); int pi=head[minj]; while (pi!=-1) { if (!(vis[e[pi].v]) && (disj+e[pi].w < node[e[pi].v].dist || disj == oo)) {node[e[pi].v].dist=disj+e[pi].w;q.push(node[e[pi].v]);} pi=e[pi].next; } } //首先循环一边找出dist最小的点 //然后循环一边对于每个点以dist为中间点更小就更新dist //从当前dist连着的边里找 } int main() { //freopen("1.txt","r",stdin); //freopen("2.txt","w",stdout); freopen("trap.in","r",stdin); freopen("trap.out","w",stdout); init(); makeit(); for (int i=1;i<=m*m;i++) { node[i].num=i; node[i].dist=oo; } node[0].num=0; dij(0); ans=oo; for (int i=m*m-m+1;i<=m*m;i++) if (node[i].dist 
           
             =ans) printf("%d\n",tot-ans); else printf("Dangerous!\n"); return 0; } /* for (int i=1;i<=ne;i++) printf("%d %d %d\n",i,e[i].v,e[i].w); for (int i=1;i<=m;i++) { for (int j=1;j<=m;j++) { printf("%d ",node[(i-1)*m+j].k); } printf("\n"); } for (int i=1;i<=m*m;i++) printf("%d %d\n",i,node[i].dist); */ /* void makeit() { for (int i=1;i<=m;i++) insert(0,i,cost[node[i].k]*cnt[node[i].k]); for (int i=1;i<=p;i++) { if (cnt[i]>0) { for (int j=0;j 
             
            
           
          
         
       
      
      
     
     
    
    
   
   

——忽然一夜清香发,散作乾坤万里春



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值