【BZOJ4773】負環-倍增+Floyd
阿新 • • 發佈:2018-11-07
測試地址:負環
做法: 本題需要用到倍增+Floyd。
我們很快能想出
的演算法:令
為走
條邊,從
走到
的路徑中最小的權值和。從小到大列舉
轉移即可。
然而並過不了,而且我們發現,負環的長度似乎也不是單調的,即存在長為
的負環,不一定表示存在長為
的負環。實際上,我們只要給每個點加一個邊權為
的自環,就可以解決這個問題了。
發現這個性質單調後,我們有兩種思路:一是二分答案,二是倍增。但我們發現二分答案需要構造走
步時的鄰接矩陣(也就是上面寫的
組成的矩陣),和上面相比複雜度沒有區別,因此我們排除這一思路,考慮倍增。
要使用倍增,需要明確一個問題:我們知道行走分兩個階段,得到第一個階段和第二個階段的鄰接矩陣,如何把它們合併為整個行走的鄰接矩陣呢?這時,我們可以使用一種類似Floyd,也類似於矩陣乘法的一個演算法,即列舉中間點
,然後列舉整個過程的起點
和終點
轉移。我們發現這個東西和矩陣乘法非常類似,它也和矩陣乘法一樣滿足結合律,但不滿足交換律,因此我們採用類似矩陣快速冪的倍增演算法,先
處理出走
次的鄰接矩陣,然後從高到低列舉
,如果當前行走的矩陣與走
次的鄰接矩陣合併後,圖中沒有出現負環,則把當前行走的矩陣與走
次的矩陣合併作為新的當前行走的矩陣,即表示走了
步。最後,演算法中行走的步數
就是答案。注意答案比
大時,顯然就表示無解。於是我們就以
的時間複雜度解決了這一題。
以下是本人程式碼:
#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,finalans=0;
struct matrix
{
int dis[310][310];
}M[10],E,now,ans;
void mult(matrix A,matrix B,matrix &S)
{
S=E;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
S.dis[i][j]=min(S.dis[i][j],A.dis[i][k]+B.dis[k][j]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
E.dis[i][j]=inf;
E.dis[i][i]=0;
}
M[0]=E;
for(int i=1;i<=m;i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
M[0].dis[x][y]=w;
}
for(int i=1;i<=9;i++)
mult(M[i-1],M[i-1],M[i]);
now=E;
for(int i=9;i>=0;i--)
{
mult(now,M[i],ans);
bool flag=0;
for(int j=1;j<=n;j++)
if (ans.dis[j][j]<0)
{
flag=1;
break;
}
if (!flag) finalans+=(1<<i),now=ans;
}
if (finalans>n) printf("0");
else printf("%d",finalans+1);
return 0;
}