負權環的判斷方法
根據鬆弛次數
n代表節點個數。
根據鬆弛次數是否大於等於n來判斷負權環,是從網上其他博客說的,根據出隊次數是否大於等於n來判斷,想到的。因爲,判斷出隊次數,是判斷更新次數的上界。
用一個n大小的數組來代表每個點的鬆弛次數。因爲SPFA算法裏的鬆弛,和Bellman-ford算法裏的鬆弛一樣。Bellman-ford算法裏,對同一個點的鬆弛次數,在極端情況下,可以想象把這些鬆弛次數分配到每一次迭代求解中去,而迭代求解一共只有n-1次。所以一旦某個點的鬆弛次數等於了n,那麼就說明有負環。
所以,在每次進行鬆弛後,遍歷判斷每個點的鬆弛次數,如果有一個等於n(再執行下去就會大於n),就說明有負環。
題目解析
題目鏈接,這道題還是比較有意思的,以每種貨幣爲節點,一個兌換所提供兩條邊,我們需要找到這個圖中是否有一個環使得我們的money可以無限增多,從負環到這裏的變化,無非是改變了鬆弛條件,建好圖,利用上述的性質便可得到。
#define ll long long
#define vec vector<int>
#define P pair<int,int>
#define MAX 105
int N, M, S, A, B, vis[MAX], num[MAX];
double V, c, a1, a2, a3, a4, mon[MAX];
struct edge {
int to; double rate, cost;
edge(int a = 0, double b = 0, double c = 0) { to = a, rate = b, cost = c; }
};
vector<edge> G[MAX];
void addEdge(int a,int b, double a1,double a2, double a3, double a4) {
G[a].push_back(edge(b, a1, a2));
G[b].push_back(edge(a, a3, a4));
}
void spfa() {
memset(mon, 0, sizeof(mon));
memset(vis, 0, sizeof(vis));
memset(num, 0, sizeof(num));
queue<int> q; q.push(S);
mon[S] = V; vis[S] = 1;
int cnt = -1;
while (!q.empty()) {
int u = q.front(); vis[u] = 0; q.pop(); num[u]++;
if (num[u] > N) { cnt = u; break; };//存在一條可以不斷增加money的路
//注意這裏是先num++的,因此大於N+1時才結束
for (int i = 0; i < G[u].size(); i++) {
edge e = G[u][i];
if ((mon[u] - e.cost)*e.rate > mon[e.to]) {
mon[e.to] = (mon[u] - e.cost)*e.rate;
if (!vis[e.to]) {
q.push(e.to);
vis[e.to] = 1;
}
}
}
}
if (cnt == -1)cout << "NO" << endl;
else cout << "YES" << endl;
}
int main() {
while (cin >> N >> M >> S >> V) {
//以錢幣的種類爲點
for (int i = 1; i <= N; i++)G[i].clear();
for (int i = 1; i <= M; i++) {
cin >> A >> B >> a1 >> a2 >> a3 >> a4;
addEdge(A, B, a1, a2, a3, a4);
}
spfa();
}
}