HDU1317XYZZY【Bellheman_ford 判断正环小应用】

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1317

题意:   

    有 N 个房间, 编号从 1 到 N 。

    每次进入一个房间, 能量值可能增加也可能减少
    问:从第一个房间开始走, 给你 100 个能量值。
        问你是否能走到第 N 个房间。


    第一行 N  输入房间的个数
    然后下面 N 行数据:
        第 i  行数据的第一个表示进入该房间得到的能量【可正可负】
        第二个表示从该房间出发能到达的房间个数 num
        剩下 num 个数表示可以到达的房间编号

算法:Bellman_ford() 判断正环


注意:有向图,
      然后建图的时候要注意下, 边是没有权的。。。
      点有权

思路:


      其实开始没看题目的时候,没有看到群里的吐槽也不会想到用 Bellman_ford()
      如果图中存在正环, 那么就可以不停的走这个环来增加能量,
      如果环中的点能到达 N 那么肯定是赢了。。。
      但是由于这个先入为主的思想,开始很容易的就让我忽略了,这题的本质是到达 N 的时候还有能量。
      然后就是各种不注意,各种 WA 的血泪史。。。。然后网上各种高深的题解。
      直到看到了一篇用 Bellman_ford 写的


      加边建图的过程同时记录连通性, 先判断 1 与 N 在不考虑能量的时候是否连通【不判断也可以,只是个无关紧要的优化】
      然后就是套用 Bellman_ford() 判断是否有正环


      注意当不存在正环的时候, 不要像以前用这个算法时直接跳出
            因为我们的主要目的不是判断正环,而是要使得到达 N 还有能量。



      那么赢的可能性就两种了:
      1.没有正环, 但是通过Bellman_ford() 的松弛操作, 到 N 的能量值 > 0
      2.存在正环, 正环中的点, 能到达终点。



    #include<stdio.h>  
    #include<string.h>  
    #include<iostream>  
    using namespace std;  
      
    const int maxn = 110;  
    const int INF = 10000000000;  
      
    int w[maxn][maxn]; // 判断图的连通性  
    int en[maxn]; //进入该点的能量值  
    int d[maxn];  //每一点的能量值  
    int n,m; //n 个点, m 条边  
      
    struct Edge{  
        int u,v;  
    }edge[maxn*maxn];  
      
    void floyd() // 有向图的传递闭包  
    {  
        for(int k = 1; k <= n; k++)  
            for(int i = 1; i <= n; i++)  
                for(int j = 1; j <= n; j++)  
                    w[i][j] = w[i][j] || (w[i][k] && w[k][j]);  //不要写错了 WA 的都是泪。。。  
    }  
      
    bool Bellman_ford()  
    {  
        for(int i = 1; i <= n; i++) d[i] = -INF;  
        d[1] = 100; // 初始第一个点有 100 个能量  
      
        for(int i = 1; i < n; i++)  
        {  
            for(int j = 0; j < m; j++)  
            {  
                int u = edge[j].u;  
                int v = edge[j].v;  
      
                if(d[v] < d[u]+en[v] && d[u]+en[v] > 0) //松弛  
                    d[v] = d[u]+en[v]; //是加上点的权。。。  
            } //注意:不能像以前一样不能松弛了,就直接返回 false 因为判断正环的目的是使 d[n] > 0  
        }  
      
        for(int i = 0; i < m; i++)  
        {  
            int u = edge[i].u;  
            int v = edge[i].v;  
      
            if(d[v] < d[u]+en[v] && d[u]+en[v] > 0) //如果存在正环  
                if(w[v][n]) //正环中的点能够到达终点  
                    return true;  
        }  
      
        return d[n]>0; // 不存在正环, 判断能否依靠 100 个能量值到达终点  
    }  
      
    int main()  
    {  
        while(scanf("%d", &n) != EOF)  
        {  
            if(n == -1) break;  
      
            m = 0; // 初始化边  
            memset(w, 0, sizeof(w));  
            memset(en, 0, sizeof(en));  
            for(int i = 1; i <= n; i++) w[i][i] = 1;  
      
            int num;  
            for(int i = 1; i <= n; i++)  
            {  
                int v;  
                scanf("%d%d", &en[i], &num);  
                while(num--) //注意是单向的  
                {  
                    scanf("%d", &v);  
                    edge[m].u = i;  
                    edge[m++].v = v;  
                    w[i][v] = 1; // 有向图, 不要傻逼的加上 w[v][i] = 1  
                }  
            }  
      
            floyd(); // 考查有向图的连通性  
            /* 
            if(!w[1][n]) 
            { 
                printf("hopeless\n"); continue; 
            }*/  
      
            if(Bellman_ford()) printf("winnable\n");  
            else printf("hopeless\n");  
        }  
        return 0;  
    }  
原文出处: 点击链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值