1797: [Ahoi2009]Mincut 最小割
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1244 Solved: 512
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
1 2 3
1 3 2
2 4 4
2 5 1
3 5 5
4 6 2
5 6 3
Sample Output
1 0
0 0
1 0
0 0
1 0
1 0
HINT
设第(i+1)行输入的边为i号边,那么{1,2},{6,7},{2,4,6}是仅有的三个最小代价切割方案。它们的并是{1,2,4,6,7},交是 。
【数据规模和约定】
测试数据规模如下表所示
数据编号 N M 数据编号 N M
1 10 50 6 1000 20000
2 20 200 7 1000 40000
3 200 2000 8 2000 50000
4 200 2000 9 3000 60000
5 1000 20000 10 4000 60000
Source
求最小割的可行边和必须边。
最小割:
最小割是割掉几条边,使得s和t不连通,并且割掉边的流量之和最小。且最小割等于最大流。
现在来考虑本题的要求。
把每个边看成粗细不同的水管,管子的粗细表示流量的大小,那么一条路径上的最大流量就是由最细的管子决定的。
因此这个最细的管子是满流的。
感性的想,要用最小的代价使水无法从s流到t,把这些最细的管子割掉就好了。
因此可行边和必须边一定是满流的。
先看可行边:
如果割掉这条边,还能从这条边的from到达to,这样割一点意义都没有。所以可行边被割掉之后,(在残量网络中)一定不能从他的from到达to。
再看必须边:
如果在残量网络中,s能到达这条边的from,这条边的to能到达t,说明这条边是他所在的路径上流量最小的边(唯一的),那么一定要割掉这条边。
因此这道题我们只要在残量网络中跑一边tarjan(因为有反向边,所以有环),根据上述条件判断对应点是否在同一个强连通分量中即可。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#define M 1000+5
#define inf 0x3f3f3f3f
#include <stack>
using namespace std;
stack<int> S;
int tot=1,scc_cnt,s,t,h[M],low[M],dfs_clock,dfn[M];
int scc[M],vt[M],v[M],d[M],cur[M],n,m;
struct edge
{
int from,to,cap,flow,ne;
}E[200005];
void Addedge(int from,int to,int cap)
{
E[++tot]=(edge){from,to,cap,0,h[from]};
h[from]=tot;
E[++tot]=(edge){to,from,0,0,h[to]};
h[to]=tot;
}
bool bfs()
{
queue<int> q;
for (int i=1;i<=n;i++)
v[i]=0;
v[s]=1;
d[s]=0;
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=h[x];i;i=E[i].ne)
{
edge e=E[i];
if (!v[e.to]&&e.cap>e.flow)
{
v[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return v[t];
}
int dfs(int x,int a)
{
if (x==t||!a) return a;
int flow=0;
for (int &i=cur[x];i;i=E[i].ne)
{
edge &e=E[i];
if (d[e.to]!=d[x]+1) continue;
int f=dfs(e.to,min(a,e.cap-e.flow));
if (f)
{
a-=f;
e.flow+=f;
E[i^1].flow-=f;
flow+=f;
if (!a) break;
}
}
return flow;
}
void dinic()
{
while (bfs())
{
for (int i=1;i<=n;i++)
cur[i]=h[i];
dfs(s,inf);
}
}
void tarjan(int x)
{
dfn[x]=low[x]=++dfs_clock;
S.push(x);
for (int i=h[x];i;i=E[i].ne)
{
edge e=E[i];
if (e.cap-e.flow<=0) continue;
if (!dfn[e.to])
{
tarjan(e.to);
low[x]=min(low[x],low[e.to]);
}
else
if (!scc[e.to])
low[x]=min(low[x],dfn[e.to]);
}
if (dfn[x]==low[x])
{
scc_cnt++;
while (!S.empty())
{
int now=S.top();
S.pop();
scc[now]=scc_cnt;
if (now==x)
break;
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
for (int i=1;i<=m;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
Addedge(x,y,c);
}
dinic();
for (int i=1;i<=n;i++)
if (!dfn[i])
tarjan(i);
for (int i=2;i<=tot;i+=2)
{
edge e=E[i];
if (e.cap==e.flow&&scc[e.to]!=scc[e.from])
printf("1");
else
printf("0");
if (e.cap==e.flow&&scc[e.from]==scc[s]&&scc[e.to]==scc[t])
printf(" 1\n");
else
printf(" 0\n");
}
return 0;
}
感悟:
1.TLE是点数看错,数组开小