LightOJ - 1074 SPFA spfa判负环 + dfs染色法 + 最短路 有关染色法和负环的实验 多组输入切勿忘记初始化

题目

在这里插入图片描述

题解思路

1. 由于是立方差,所以存在负环,迪杰斯特拉就失效了。SPFA启动!
2. 对于负环上的点,我们要进行染色处理(当这个点入队的次数大于N时,即用DFS跑出这个点能延申的所有区域)
3. 负环,又叫负权回路,负权环,指的是一个图中存在一个环,里面包含的边的边权总和<0。
4. 用染色法,将出现负环的点的连通分量全部染色,遇到被染色的点直接跳过!
5. 多组样例,一定不要忘记初始化!!!
关于 染色法

在这里插入图片描述
因为存在负环导致1 2 3 4 的值都是假的 ,进而导致 5的值也是假的(尽管5不是环中的元素),因为从1开始走必须要经过2或者1才能到5,所以只要是负环中的元素能到达的点,全都是假的值。用DFS搜索能到达的点然后标记即可!
下面展示一个不是和原点直接相连的负环,2 3 4 5 构成负环 ,6本来不是负环中的元素也受到了3的影响,变成了负值,所以,只要是负环元素相连的点都是假的答案,都必须染色来去掉!

在这里插入图片描述

在这里插入图片描述

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const  int  INF =  0x3f3f3f3f;
int a[210];
struct bian
{
    int w;
    int z;
};
vector <bian> head[210];
bool vis[210];
bool vis2[210];
int dis[210];
int cnt[210];
int n,m;
void dfs(int x)
{
    for (int i = 0 ; i < head[x].size() ;i++ )
    {
        int tmp = head[x][i].z;
        if (vis2[tmp])
            continue;
        vis2[tmp] = 1;
        dfs(tmp);
    }
}
void spfa ()
{
    memset(vis,0,sizeof(vis));
    memset(vis2,0,sizeof(vis2));
    memset(cnt,0,sizeof(cnt));
    for (int i = 1 ;i <= n ; i++ )
        dis[i] = INF ;
    queue <int> q;
    dis[1] = 0;
    cnt[1]++;
    q.push(1);
    vis[1] = 1;
    int tmm;
    while(!q.empty())
    {
         tmm = q.front();
         q.pop();
         vis[tmm] = 0;
         if (vis2[tmm])
            continue;
         for ( int i = 0 ; i < head[tmm].size() ; i++  )
         {
        /*    int tmp = dis[tmm] + head[tmm][i].w;
                 printf("tmm = %d  " ,tmm);
                printf("tmp = %d  " ,tmp);
                 printf("  %d  \n",dis[head[tmm][i].z]);  */
             if (dis [head[tmm][i].z] > dis[tmm] + head[tmm][i].w )
             {
               dis [head[tmm][i].z] = dis[tmm] + head[tmm][i].w;
                if (cnt[head[tmm][i].z] > n)
                {
                    dfs(head[tmm][i].z);
                    continue;
                }
               if (vis[head[tmm][i].z] == 0)
               {
           //      printf("入队的点为%d  \n",head[tmm][i].z);
                   q.push(head[tmm][i].z);
                   cnt[head[tmm][i].z]++;
                   vis[head[tmm][i].z] = 1;
               }
             }
         }
  /*     for (int i = 1 ;i <= n ; i++ )
        cout<<dis[i]<<"   ";
        cout<<"\n";  */
    }
}
int main ()
{
    int f,sum = 1;;
    cin>>f;
    while(f--)
    {
        cin>>n;
        for (int i = 1 ; i <= n ; i++ )
            cin>>a[i];
        cin>>m;
        bian t;
        for (int i = 1 ; i <= m ; i++ )
        {
            int s1,s2;
            cin>>s1>>s2;
            t.w = -(a[s1]-a[s2])*(a[s1]-a[s2])*(a[s1]-a[s2]);
            t.z = s2;
            head[s1].push_back(t);
        }
        spfa();
        int p;
        cin>>p;
        cout<<"Case "<<sum<<":\n";
        for (int i = 1 ;i <= p ; i++ )
        {
            int pot;
            cin>>pot;
            if ( dis[pot] < INF && cnt[pot] <= n && dis[pot] >= 3 && vis2[pot] == 0 )
            {
                cout<<dis[pot]<<"\n";
            }else
            cout<<"?\n";
        }
        sum++;
        for (int i = 1 ;i <= n ; i++ )
            head[i].clear();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值