1. 程式人生 > >【XSY2708】hack 網絡流

【XSY2708】hack 網絡流

mar endif 圖片 oid while freopen span 連通 int

題目描述

  給你一個圖,每條邊有一個權值。要求你選一些邊,滿足對於每條從\(1\)\(n\)的路徑上(可以不是簡單路徑)有且僅有一條被選中的邊。問你選擇的邊的邊權和最小值。

  \(n\leq 100\)

題解

  先把整張圖分為兩個集合\(S,T\),其中\(S\)是從原點開始BFS能夠到達的點組成的集合,\(T\)是剩下的點組成的集合。

  如果沒有在一條路徑上只能選一條邊的限制,就是一個普通的網絡流了。

  我們看看什麽情況下這個條件不會被滿足。

  技術分享圖片

  上面這個圖中我們選擇了\((1,2)\)\((4,6)\)\(S=\{1,3,4\},T=\{2,5,6\}\)

  可以發現如果多次從\(S\)

走到\(T\)(比如上面這張圖中\(1\rightarrow2\rightarrow4\rightarrow6\)),那麽這些\(S\rightarrow T\)的邊就都被選中同時在同一條路徑上。所以不合法。

  所以一旦走到\(T\)後就不能走回\(S\)。  

  如果一條邊從\(T\)指向\(S\),那麽這條邊的反向邊就滿流了。

  為了避免這種情況,只需要把反向邊的容量設為\(\infty\)

  坑點:如果一條邊的兩個斷點與\(S\)\(T\)不連通,就不要連邊。

  時間復雜度:\(O(\)能過\()\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm> #include<queue> using namespace std; typedef long long ll; const ll inf=1e15; namespace flow { int v[100010]; ll c[100010]; int t[100010]; int h[100010]; int n; void add(int x,int y,ll a) { n++; v[n]=y; c[n]=a; t[n]=h[x]; h[x]=n; } int
d[100010]; int e[100010]; int op(int x) { return ((x-1)^1)+1; } int S,T; queue<int> q; int num; int cur[100010]; void bfs() { memset(d,-1,sizeof d); d[T]=0; q.push(T); int x,i; while(!q.empty()) { x=q.front(); q.pop(); e[d[x]]++; for(i=h[x];i;i=t[i]) if(c[op(i)]&&d[v[i]]==-1) { d[v[i]]=d[x]+1; q.push(v[i]); } } } ll dfs(int x,ll flow) { if(x==T) return flow; ll s=0,u; for(int &i=cur[x];i;i=t[i]) if(c[i]&&d[v[i]]==d[x]-1) { u=dfs(v[i],min(flow,c[i])); s+=u; flow-=u; c[i]-=u; c[op(i)]+=u; if(!flow) return s; } e[d[x]]--; if(!e[d[x]]) d[S]=num; d[x]++; e[d[x]]++; cur[x]=h[x]; return s; } ll solve() { ll ans=0; bfs(); memcpy(cur,h,sizeof h); while(d[S]>=0&&d[S]<=num-1) ans+=dfs(S,inf); return ans; } } void add(int x,int y,int c) { flow::add(x,y,c); flow::add(y,x,inf); } int f[110][110]; int lx[2510]; int ly[2510]; int lz[2510]; int n,m; int main() { #ifndef ONLINE_JUDGE freopen("b.in","r",stdin); freopen("b.out","w",stdout); #endif scanf("%d%d",&n,&m); int i,j,k; for(i=1;i<=m;i++) { scanf("%d%d%d",&lx[i],&ly[i],&lz[i]); lx[i]++; ly[i]++; f[lx[i]][ly[i]]=1; } for(k=1;k<=n;k++) for(i=1;i<=n;i++) if(i!=k&&f[i][k]) for(j=1;j<=n;j++) if(j!=i&&j!=k) f[i][j]|=f[i][k]&&f[k][j]; for(i=1;i<=n;i++) f[i][i]=1; flow::S=1; flow::T=n; flow::num=n; for(i=1;i<=m;i++) if(f[1][lx[i]]&&f[ly[i]][n]) add(lx[i],ly[i],lz[i]); ll ans=flow::solve(); if(ans>=inf) printf("-1\n"); else printf("%lld\n",ans); return 0; }

【XSY2708】hack 網絡流