【BZOJ4773】负环 倍增Floyd

【BZOJ4773】负环

Description

在忘记考虑负环之后,黎瑟的算法又出错了。对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得

环上的边权和为负数。保证图中不包含重边和自环。

Input

第1两个整数n, m,表示图的点数和边数。

接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边。

2 <= n <= 300

0 <= m <= n(n <= 1)

1 <= ui, vi <= n

|wi| <= 10^4

Output

仅一行一个整数,表示点数最小的环上的点数,若图中不存在负环输出0。

Sample Input

3 6
1 2 -2
2 1 1
2 3 -10
3 2 10
3 1 -10
1 3 10

Sample Output

2

题解:我承认最近做矩乘有点多了~

看时间复杂度显然是O(n³㏒n)可以搞的,所以直接上倍增Floyd,具体方法有点像用倍增求LCA。就是先预处理出邻接矩阵的2次方,4次方,2^n次方。。。然后在不断从大到小去试,如果ans*转移矩阵的2^j次方不存在负环,则ans就乘上邻接矩阵的2^j次方,否则不乘。最后只要在乘上邻接矩阵的一次方,就一定会出现负环了

但仔细思考这个方法,发现貌似不满足单调性,也就是可能存在长度为5的负环,却不存在长度为6的负环,因此我们只要连一条从i到i长度为0的边,即让邻接矩阵的map[i][i]=0,就可以使它满足单调性了(其实正常的邻接矩阵都应该这么搞~)

听说O(n³㏒²n)也能过,难道是我的代码自带大常数?跑了7000多ms~

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,m,ans;
typedef struct matrix
{
    int v[310][310];
}M;
M f[12],x,y,emp;
M mmul(M a,M b)
{
    M c=emp;
    int i,j,k;
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                c.v[i][j]=min(c.v[i][j],a.v[i][k]+b.v[k][j]);
    return c;
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(emp.v,0x3f,sizeof(emp.v));
    f[0]=x=emp;
    int i,a,b,c,j;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        f[0].v[a][b]=c;
    }
    for(i=1;i<=n;i++)    f[0].v[i][i]=x.v[i][i]=0;
    for(j=1;(1<<j)<=n;j++)
        f[j]=mmul(f[j-1],f[j-1]);
    for(j=j-1;j>=0;j--)
    {
        y=mmul(x,f[j]);
        for(i=1;i<=n;i++)
            if(y.v[i][i]<0)  break;
        if(i==n+1)  x=y,ans+=(1<<j);
    }
    if(ans>n)    printf("0");
    else    printf("%d",ans+1);
    return 0;
}
时间: 2024-10-07 03:36:46

【BZOJ4773】负环 倍增Floyd的相关文章

bzoj4773 负环 倍增+矩阵

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4773 题解 最小的负环的长度,等价于最小的 \(len\) 使得存在一条从点 \(i\) 到自己存在一条长度 \(\leq len\) 的负权路径. 为了把 \(\leq len\) 转化为 \(=len\),我们可以给每一个点建立有个边权为 \(0\) 的自环. 所以考虑倍增邻接矩阵,维护两点之间的经过 \(2^i\) 条边的最短路. 倍增的时候判断走了那么多步有没有负环就可以了. 最后结

BZOJ4773: 负环

倍增floyd裸题,倍增判断走2^i步是否存在负环就好了. 其实和3763是一样的,然而那题数据挂了. #include<cstdio> void upd1(int&a,int b){ if(a>b)a=b; } const int N=301; typedef int arr[N][N]; int n; void pre(arr a){ for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=1e9; } void d

HDU 1217 Arbitrage(Bellman-Ford判断负环+Floyd)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1217 题目大意:问你是否可以通过转换货币从中获利 如下面这组样例: USDollar 0.5 BritishPound BritishPound 10.0 FrenchFranc FrenchFranc 0.21 USDollar 可以通过US->Br->French->US这样转换,把1美元变成1*0.5*10*0.21=1.05美元赚取%5的利润. 解题思路:其实就相当于bellman-

BZOJ_4773_负环_倍增弗洛伊德

BZOJ_4773_负环 Description 在忘记考虑负环之后,黎瑟的算法又出错了.对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得 环上的边权和为负数.保证图中不包含重边和自环. Input 第1两个整数n, m,表示图的点数和边数. 接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边. 2 <= n <= 300 0 <= m <= n(n <= 1) 1 <= ui, vi <

ACM: POJ 3259 Wormholes - SPFA负环判定

POJ 3259 Wormholes Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Description While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way pa

负环 BZOJ 4773

负环 [问题描述] 在忘记考虑负环之后,黎瑟的算法又出错了.对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得环上的边权和为负数.保证图中不包含重边和自环. [输入格式] 第1两个整数n, m,表示图的点数和边数. 接下来的m行,每<=三个整数ui, vi, wi,表<=有一条从ui到vi,权值为wi的有向边. [输出格式] 仅一行一个整数,表示点数最小的环上的点数,若图中不存在负环输出0. [样例输入] 3 6 1 2 -2 2 1 1 2 3 -10 3 2 10 3

bzoj 1486: [HNOI2009]最小圈 dfs求负环

1486: [HNOI2009]最小圈 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1022  Solved: 487[Submit][Status] Description 最开始写floyd求负环结果TLE了,改成dfs后速度变成原来的100+倍.反正还是比较神奇.

poj 3259 Wormholes(bellman-ford判断负环)

题目链接:http://poj.org/problem?id=3259 题目就是问你能否回到原点而且时间还倒回去了.题目中有些路中有单向的虫洞能让时间回到过去 所以只要将虫洞这条边的权值赋为负然后再判断有没有负环就行了. #include <iostream> #include <cstring> using namespace std; const int inf = 10001; int f , n , m , w ,dis[1001] , counts; struct TnT

(简单) LightOJ 1074 Extended Traffic,SPFA+负环。

Description Dhaka city is getting crowded and noisy day by day. Certain roads always remain blocked in congestion. In order to convince people avoid shortest routes, and hence the crowded roads, to reach destination, the city authority has made a new