【题目大意】 有 N 个城市,M 个中转站以及 L 条有向边(u, v, c),表示可以从 u 向 v 传送信息, 带宽为 c。每个城市都在向 CIA 总部发送无穷大的信息量,但是目前总部实际接 收带宽已经不能满足要求。CIA 决定要增大某条边的带宽以增大总部的接收带宽, 请找出哪些边带宽的增加能导致总部接收带宽的增加。(1 <= N+M <= 100, 1 <= L <= 1000)
【建模方法】 此题要求找出这样一条边,增加它的容量可以导致最大流的增加。最直观的做法 是先求一次最大流记为 ans,然后枚举每条边的容量加 1 再求最大流,看是否大 于 ans。但是这样做复杂度太高。有没有更好的办法?如果我们换个角度,考虑 求完最大流后残量网络 R 中的割,那么问题便迎刃而解。最大流的含义是我们现 在在 R 中找不到一条从 s 走到 t 的增广路。我们只要找到这样的割边(u, v),使得 R 中从 s 能走到 u,从 v 能走到 t,那么当我增加这条割边的容量后,它就会再次 出现在 R 中,架起一条增广路 s-u-v-t,从而增加最大流的流量。再正式描述一下 算法流程:求一次最大流,然后在残量网络 R 中以正向弧对 s、以正向弧的逆对 t 作 DFS,并用 from[i], to[i]标记点 i 能否从 s 可达,能否可达 t。扫描每一条正向 弧(u, v),若它残留容量为 0 且 from[u]且 to[v],则(u, v)为一个解。
发散思维:
如何判定网络的最小割是否唯一? 同样求一次最大流后在残量网络 R 中以正向弧对 s、以正向弧的逆对 t 作 DFS, 只不过这次只用一个 flag[i]标记点 i 是否在两次 DFS 中被探访过。最后扫一遍所 有点,如果存在没有被探访过的,则说明最小割不唯一。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
const int N=1e5;
const int inf=0x3f3f3f3f;
struct node
{
int to,cap,next,flow;
node(){}
node(int to,int cap,int next,int flow):to(to),cap(cap),next(next),flow(flow){}
}edge[N];
int tol;
int head[N],gap[N],dep[N],pre[N],cur[N];
bool from[N],to[N],vis[N];
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w,int rw=0)
{
edge[tol]=node(v,w,head[u],0);
head[u]=tol++;
edge[tol]=node(u,rw,head[v],0);
head[v]=tol++;
}
int sap(int start,int _end,int N)
{
memset(gap,0,sizeof(gap));
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(head));
int u=start;
pre[u]=-1;
gap[0]=N;
int ans=0;
while(dep[start]<N)
{
if(u==_end)
{
int Min=inf;
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
Min=min(Min,edge[i].cap-edge[i].flow);
for(int i=pre[u];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=Min;
edge[i^1].flow-=Min;
}
u=start;
ans+=Min;
continue;
}
bool flag=false;
int v;
for(int i=cur[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u])
{
flag=true;
cur[u]=pre[v]=i;
break;
}
}
if(flag)
{
u=v;continue;
}
int Min=N;
for(int i=head[u];i!=-1;i=edge[i].next)
if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min)
{
Min=dep[edge[i].to];
cur[u]=i;
}
gap[dep[u]]--;
if(!gap[dep[u]])return ans;
dep[u]=Min+1;
gap[dep[u]]++;
if(u!=start)u=edge[pre[u]^1].to;
}
return ans;
}
queue<int>q;
vector<int>ans;
int main()
{
//freopen("a.txt","r",stdin);
int n,m,l;
while(cin>>n>>m>>l)
{
if(n==0&&m==0&&l==0)break;
init();
for(int i=1;i<=l;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
}
for(int i=1;i<=n;i++)addedge(n+m+1,i,inf);
int sum=sap(n+m+1,0,n+m+2);
memset(from,0,sizeof(from));
memset(to,0,sizeof(to));
while(!q.empty())q.pop();
q.push(n+m+1);
while(!q.empty())
{
int top=q.front();q.pop();
from[top]=1;
for(int i=head[top];i!=-1;i=edge[i].next)
{
if(i&1)continue;
int v=edge[i].to;
if(!from[v]&&edge[i].cap>edge[i].flow)q.push(v);
}
}
while(!q.empty())q.pop();
q.push(0);
while(!q.empty())
{
int top=q.front();q.pop();
to[top]=1;
for(int i=head[top];i!=-1;i=edge[i].next)
{
if(i%2==0)continue;
int v=edge[i].to;
if(!to[v]&&edge[i^1].cap-edge[i^1].flow>0)q.push(v);
}
}
memset(vis,0,sizeof(vis));
int flag=0;
for(int i=0;i<=n+m;i++)
{
for(int j=head[i];j!=-1;j=edge[j].next)
{
if(j>=0&&j<2*l&&j%2==0&&from[i]&&to[edge[j].to]&&edge[j].cap==edge[j].flow)
vis[j]=1,flag=1;
}
}
if(flag==0){puts("");continue;}
for(int i=0;i<2*l;i+=2)if(vis[i])
{
if(flag==0)printf(" ");
else flag=0;
printf("%d",i/2+1);
}
puts("");
}
return 0;
}