【XSY2708】hack 網絡流
阿新 • • 發佈:2018-03-06
mar endif 圖片 oid while freopen span 連通 int 走到\(T\)(比如上面這張圖中\(1\rightarrow2\rightarrow4\rightarrow6\)),那麽這些\(S\rightarrow T\)的邊就都被選中同時在同一條路徑上。所以不合法。
題目描述
給你一個圖,每條邊有一個權值。要求你選一些邊,滿足對於每條從\(1\)到\(n\)的路徑上(可以不是簡單路徑)有且僅有一條被選中的邊。問你選擇的邊的邊權和最小值。
\(n\leq 100\)
題解
先把整張圖分為兩個集合\(S,T\),其中\(S\)是從原點開始BFS能夠到達的點組成的集合,\(T\)是剩下的點組成的集合。
如果沒有在一條路徑上只能選一條邊的限制,就是一個普通的網絡流了。
我們看看什麽情況下這個條件不會被滿足。
上面這個圖中我們選擇了\((1,2)\)和\((4,6)\)。\(S=\{1,3,4\},T=\{2,5,6\}\)。
可以發現如果多次從\(S\)
所以一旦走到\(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 網絡流