1. 程式人生 > >【BZOJ4773】負環 倍增Floyd

【BZOJ4773】負環 倍增Floyd

方法 family 包含 -s sharp 有向圖 。。 ret space

【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;
}

【BZOJ4773】負環 倍增Floyd