这道题目是一道最小割,弱弱的建图还是想了很久,其实建图很简单,只需要把可以取到的点连到一个超级汇点上,容量是那个城市的价值,这样求完最小割就可以保证从源点到汇点无通路,一种情况是,城市选取,道路被隔断,还有一种情况是城市被割掉了。这道题还要去求出任意一个割集。
之前虽然网络流学了一段时间,发现对算法原理上的东西理解还是太不透彻,刚开始自己的做法就是,因为在最大流跑完之后,原来加入的那些道路相当于是就被堵塞了,所以就砸残留网络里面的那些正向边上进行dfs,自己试了几个例子都没问题,返回WA,纠结很久,实在没有办法改成了直接在残留网络上进行dfs,结果ac,仔细想了一想,前面的想法其实不太对,反向边是必须考虑的,举个例子可以知道,黑色的是增广路径,如果不考虑反向边,C无法染色,D无法染色,点集就分成AB 和CD两个,那么割集就存
在AC和BD两条割边,如果考虑反向边就不会出现这种情况了,割边只有一条BD。
因为最开始的想法是最小割把点集分成了两部分,但是无形中就把残留网络和最小割直接等价起来了, 其实不对,所以这个地方必须去考虑反向边,另外一方面,isap以及ek在求增广路径的时候也是同个整个残留网络流求解的,这也就是说isap在最后一次寻找增广路径时没有办法到达汇点t,也就是说明了在残留网络中点集分成了两个集合。
整理一下思路,这道题先跑最大流,在残留网络中,从源点开始dfs,把能到达的点开始染色,如上图,然后剩下的正向边中如果起点是染色的,终点未染色,那么就是一条割边。
代码就贴一部分关键的吧。
void dfs(int x)
{
int tem;
if(1 == hash[x])
{
return ;
}
hash[x]=1;
tem=net[x];
while(-1 != tem)
{
if(0 == edge[tem].cap)
{
tem=edge[tem].next;
continue;
}
dfs(edge[tem].v);
tem=edge[tem].next;
}
}
void print()
{
int tem,i,sum=0;
for(i=1;i<=nn;i++)
{
tem=net[i];
while(-1 != tem)
{
if(1 == (1&tem))
{
tem=edge[tem].next;
continue;
}
if(tem/2+1 > mm)
{
tem=edge[tem].next;
continue;
}
if(1 == hash[i] && 0 == hash[edge[tem].v])
{
order[sum]=tem/2+1;
sum++;
}
tem=edge[tem].next;
}
}
printf("%d",sum);
for(i=0;i<sum;i++)
{
printf(" %d",order[i]);
}
printf("\n");
}