挺无聊的一道网络流
不过有一个挺有特色的地方是要枚举汇点,觉得挺好玩的就记录下来吧
题意:有N个点,每两个点间都有一条双向边,问使这些点中至少有两个点不连通所需要破坏的边的最小权值
题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3134
取任意点为S点,若u,v两点不连通,则u,v中至少有一个与S也不连通。
因为题目中均为双向边,若u,v均与s连通,那么由路径u->s->v可知u,v也应该是连通的
所以显然S点是可以任取的,但是T点是需要枚举的。
那么就枚举T点依次求最小割,取最小值
PS:这道题写的比较早,那时候我的sap模板还很挫。。还加了dinic优化的。。后来发现稠密图用dinic实在是慢,参考了lieyan大神的代码后就去掉了bfs预处理。。
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x7f7f7f7f
using namespace std;
int p[105],d[105],gap[105],cap[105][105],f[105][105],S,T;
void bfs(int n)
{
int u,v;
memset(d,-1,sizeof(d));
memset(gap,0,sizeof(gap));
queue<int>dd;
dd.push(T);
d[T]=0;gap[0]=1;
while(!dd.empty())
{
u=dd.front();dd.pop();
for(v=1;v<=n;v++)
if((cap[v][u]>f[v][u])&&(d[v]==-1))
{
d[v]=d[u]+1;
gap[d[v]]++;
dd.push(v);
}
}
}
int sap(int n)
{
int v,u,a,mind,ans;
bfs(n);
u=S;ans=0;
memset(p,-1,sizeof(p));
while(d[S]<n)
{
for(v=1;v<=n;v++)if(cap[u][v]>f[u][v]&&d[u]==d[v]+1 ) break;
if(v<=n)
{
p[v]=u;u=v;
if(v==T)
{
int a=INF;
for(v=T;v!=S;v=p[v])a=a<(cap[p[v]][v]-f[p[v]][v])?a:(cap[p[v]][v]-f[p[v]][v]);
for(v=T;v!=S;v=p[v])
{
f[p[v]][v]+=a;
f[v][p[v]]-=a;
}
ans+=a;
u=S;
}
}
else
{
int mind=n;
for(v=1;v<=n;v++)if(cap[u][v]>f[u][v])mind=mind<d[v]+1?mind:d[v]+1;
--gap[d[u]];
if(gap[d[u]]==0)return ans;
++gap[mind];
d[u]= mind;
if(u!=S) u=p[u];
}
}
return ans;
}
int main()
{
int n,i,j,ans,x;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&cap[i][j]);
ans=INF;
S=1;
for(T=2;T<=n;T++)
{
memset(f,0,sizeof(f));
x=sap(n);
ans=ans<x?ans:x;
}
printf("%d\n",ans);
}
}