1. 程式人生 > >BZOJ_4773_負環_倍增弗洛伊德

BZOJ_4773_負環_倍增弗洛伊德

print $2 i++ opera led for 表示圖 LG +=

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 <= 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
先開$Logn$個矩陣$dis$,$dis[i][j][k]$表示從$j$到$k$走$2^{i}$條邊的最短路。 然後像倍增$lca$那樣,再從大到小找到最後一個沒有負環的矩陣,再乘一次初始矩陣,判斷有沒有負環即可。 代碼:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 350
int f[N][N][20],n,m,L[N];
struct Mat {
    int v[301][301];
    Mat(){memset(v,0x3f,sizeof(v));}
    Mat operator*(const Mat &x)const {
        Mat re;int i,j,k;
        for(k=1;k<=n;k++) {
            for(i=1;i<=n;i++) {
                for(j=1;j<=n;j++) {
                    re.v[i][j]=min(re.v[i][j],v[i][k]+x.v[k][j]);
                }
            }
        }
        return re;
    }
}dis[10];
bool judge(Mat x) {
    int i;
    for(i=1;i<=n;i++) {
        if(x.v[i][i]<0) return 1;
    }
    return 0;
}
int main() {
    scanf("%d%d",&n,&m);
    int i,x,y,z,sum=0;
    Mat tmp;
    for(i=1;i<=n;i++) dis[0].v[i][i]=tmp.v[i][i]=0;
    for(i=2;i<=n;i++) L[i]=L[i>>1]+1;
    for(i=1;i<=m;i++) {
        scanf("%d%d%d",&x,&y,&z);
        dis[0].v[x][y]=z;
    }
    for(i=1;i<=L[n];i++) dis[i]=dis[i-1]*dis[i-1];
    for(i=L[n];i>=0;i--) {
        if(!judge(tmp*dis[i])) {
            tmp=tmp*dis[i]; sum+=(1<<i);
        }
    }
    tmp=tmp*dis[0];
    printf("%d\n",judge(tmp)?sum+1:0);
}

BZOJ_4773_負環_倍增弗洛伊德