计蒜客——闯关游戏 SPFA找最长路

问题描述
蒜头君在玩一个很好玩的游戏,这个游戏一共有至多 100 个地图,其中地图 1 是起点,房间 n 是终点。有的地图是补给站,可以加 ki点体力,而有的地图里存在怪物,需要消耗 ki 点体力,地图与地图之间存在一些单向通道链接。 
蒜头君从 1 号地图出发,有 100 点初始体力。每进入一个地图的时候,需要扣除或者增加相应的体力值。这个过程持续到走到终点,或者体力值归零就会 Game Over。不过,他可以经过同个地图任意次,且每次都需要接受该地图的体力值。 
输入格式 
第 1 行一个整数 n (n≤100)。 
第 2 ~ n+1 行,每行第一个整数表示该地图体力值变化。接下来是从该房间能到达的房间名单,第一个整数表示房间数,后面是能到达的房间编号。 
输出格式 
若玩家能到达终点,输出Yes,否则输出No。 
样例输入 

0 1 2 
-60 1 3 
-60 1 4 
20 1 5 
0 0 
样例输出 
No

 

!!!!注意:

原题题意太蠢了,意思是第二行代表结点1,可以到达房间数为1,即房间2,消耗0生命值,依次类推

我改成

第 1 行两个个整数 n (n≤100) , m
下面m行,每行第一个整数表示起始房间号。接下来是终点房间号,最后一个整数为负数表示消耗生命值,正数为加生命值

改编后输入样例

7 7
1 2 20
2 5 -1000
2 3 30
3 4 40
4 2 20
5 6 -1000
6 7 -1000

输出样例

Yes

思路:将生命值变化转换成边的权重,而结点1到自身,即dis[1] = 100,然后找单源最长路,注意若图中有正环,需要特殊处理,在代码中有给出

#include <bits/stdc++.h>

using namespace std;

const int INF = 10000000;
const int N = 105;
const int M = N*N+10;

struct edge{
    int v,w,next;
}edges[M];

int head[N];
int nums = 0;

void insert_list(int u,int v,int w){  //链表头插法
    edges[nums].v = v;
    edges[nums].w = w;
    edges[nums].next = head[u];
    head[u] = nums++;
}

queue<int> q;

bool vis[N];  //判断是否已经在队列中

int cnt[N];  //存储入队次数

int dis[N];  //源点到每个点的距离

int n;

//求最长路
bool SPFA(int u){
    memset(dis,-0x3f,sizeof dis);  //求最长路
    dis[u] = 100;
    q.push(u);
    vis[u] = true;
    cnt[u]++;
    while(!q.empty()){
        int curr_v = q.front();
        q.pop();
        vis[curr_v] = false;

        if(dis[n] == INF){  //若终点在正环上或者经过正环能到终点,即刻退出
            return true;
        }

        if(cnt[curr_v] > n){  //若存在正环,此结点不再入队,否则会出现死循环
            vis[curr_v] = true;
        }

        for(int j = head[curr_v]; ~j; j = edges[j].next){
            int v = edges[j].v;
            int w = edges[j].w;
            if(dis[v] < dis[curr_v] + w){  //求最长路
                dis[v] = dis[curr_v] + w;
                if(!vis[v]){
                    q.push(v);
                    vis[v] = true;
                    cnt[v]++;
/*当这个结点入队次数超过结点数n,说明存在正环,正环代表你可以一直在这刷生命值,
若从起点到终点经过这个环,则必能通关,让这个结点的生命值开到正无限即可*/
                    if(cnt[v] > n)
                    {
                        dis[v] = INF;
                    }
                }
            }
        }
    }

    if(dis[n] > 0){
        return true;
    }
    else{
        return false;
    }

}


int main() {
    int m;
    cin>>n>>m;

    memset(head,-1,sizeof head);  //初始化头节点

    for(int i = 1; i <= m; ++i){
        int hp,rooms,to;
        cin>>rooms>>to>>hp;
        insert_list(rooms,to,hp);
    }

    if(SPFA(1)){
        cout<<"Yes"<<endl;
    }
    else{
        cout<<"No"<<endl;
    }

   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值