UVA 11478 Halum ( 二分 + 差分約束系統 )
UVa Online Judge
由於順序無關,我們可以將每個操作獨立出來,令 dsum( u ) 為作用在 u 上的所有 d 權和,那麼對於一個邊 u -> v,它的最終邊權是 w( u, v ) + dsum( u ) - dsum( v )。由於題目要求最小值極大化,我們考慮二分搜尋,假設最小值的最大可能為 x,可以列出每個邊 u -> v 都必須滿足的不等式 w( u, v ) + dsum( u ) - dsum( v ) ≥ x,由於二分搜尋可以使 x 為已知,令已知 C = w( u, v ) - x,可以寫成 dsum( u ) + C ≥ dsum( v ),是標準的差分約束系統。在差分約束系統裡,這個不等式等價於一個圖上存在一條有向邊 u -> v 且邊權為 C。可以用 SPFA求是否有負環( 因為這題不保證連通 所以我不確定怎麼求各個 dsum() 否則不難 如1201 -- Intervals ),若存在則不存在有限實數解( 可能無限大,可能無解要另外判斷 ),反之則存在有限實數解。
頭腦不太清楚造了兩個無聊的蟲,一個是想學 LRJ的二分搜( 感覺比較能變化,挺有用的 ) 結果 "while( lb <= ub )"寫成 "while( lb < ub )",另一個更智障,無解時要求輸出 "No Solution",我竟輸出 "No solution" 除錯好久。。
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> pii; const int INF = 1e9; const int MAXN = 500 + 5; const int MAXM = 2700 + 2; const int MAXD = 1e4; int n, m; vector<pii> G[MAXN]; int dis[MAXN], vis[MAXN], inq[MAXN]; bool hasNegativeCycle(){ queue<int> que; memset( dis, 0, sizeof(dis) ); memset( vis, 0, sizeof(vis) ); memset( inq, 0, sizeof(inq) ); for(int u = 1; u <= n; ++u) que.push( u ); while( !que.empty() ){ int u = que.front(); que.pop(); inq[ u ] = 0; for(pii &p: G[u]){ int c = p.first, v = p.second; if( dis[ v ] > dis[ u ] + c ){ dis[ v ] = dis[ u ] + c; if( !inq[ v ] ){ inq[ v ] = 1; que.push( v ); if( ++vis[ v ] > n ) return true; } } } } return false; } bool test(int x){ for(int u = 1; u <= n; ++u) for(pii &p: G[u]) p.first -= x; bool ok = !hasNegativeCycle(); for(int u = 1; u <= n; ++u) for(pii &p: G[u]) p.first += x; return ok; } void solve(){ if( test( MAXD + 1 ) ){ puts("Infinite"); // can grow infinitely large return; } if( !test( 1 ) ){ puts("No Solution"); // even the minimum valid is intolerable return; } /* LRJ's binary search int ans = 1; int lb = 2, ub = MAXD; while( lb <= ub ){ int mid = ( lb + ub ) / 2; if( test( mid ) ) ans = mid, lb = mid + 1; else ub = mid - 1; } printf("%d\n", ans); */ int lb = 1, ub = MAXD + 1; while( lb + 1 < ub ){ int mid = ( lb + ub ) / 2; if( test( mid ) ) lb = mid; else ub = mid; } printf("%d\n", lb); } void init(){ for(int u = 1; u <= n; ++u) G[u].clear(); } int main(){ while( scanf("%d%d", &n, &m) == 2 ){ init(); while( m-- ){ int u, v, d; scanf("%d%d%d", &u, &v, &d); G[ u ].push_back( pii( d, v ) ); } solve(); } return 0; }