题意:n点,m条有向边,n<=100,m<=5000.删除i所有入边花费a[i],删除i所有出边花费b[i],问删除所有边的最小花费?
删除边u->v代价为(a[u]或着b[v]) 则操作看成点,a[u]-b[v]连接一条边,删除所有的边相当于用点(操作)覆盖所有的边.
求二分图的最小点权覆盖即可
利用最小割模型求最小点权覆盖
删除最小割上的边,s-t不存在路径,所以(s,u),(u,v),(v,t)至少有一条边在最小割上.
最小割上的边是满流的,人为的令(u,v)不在最小割上,则(u,v)=inf
所以(s,u),(v,t)至少一条边在最小割上 对所有边u-v,u或者v至少有一个被选中满足点权覆盖,并且最小割容量最小,所以为最小点权覆盖
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
const int N=5e2+20;
const int inf=2e8;
int n,m,s,t,a[N],b[N];//a[i],b[i] 删除i所有入边和出边的代价
int flow[N][N],dis[N],vis[N];
queue<int> q;
vector<int> v;
int bfs(int s,int t)//建立层次图
{
while(!q.empty())
q.pop();
memset(dis,-1,sizeof(dis));
dis[s]=0,q.push(s);
while(!q.empty())
{
int k=q.front();
q.pop();
for(int i=1;i<=t;i++)
{
if(flow[k][i]>0&&dis[i]==-1)//可以到达并且未访问
{
dis[i]=dis[k]+1;
q.push(i);
}
}
}
if(dis[t]>=0)
return 1;
return 0;
}
int dfs(int x,int mx)//找增广路
{
int f;
if(x==t)
return mx;
int now=mx;
for(int i=1;i<=t;i++)
{
if(flow[x][i]>0&&dis[i]==dis[x]+1&&(f=dfs(i,min(mx,flow[x][i]))))
{
flow[x][i]-=f;
flow[i][x]+=f;
return f;
}
}
return 0;
}
void dfs(int u)//找最小割的割边 即满流边
{
vis[u]=1;
for(int i=0;i<=t;i++)
{
//标记所有非满流边
if(!vis[i]&&flow[u][i])
dfs(i);
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
v.clear();
memset(vis,0,sizeof(vis));
memset(flow,0,sizeof(flow));
cin>>n>>m;
s=0,t=2*n+1;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),flow[i+n][t]=a[i];
for(int i=1;i<=n;i++)
scanf("%d",&b[i]),flow[s][i]=b[i];
while(m--)
{
int u,V;
scanf("%d%d",&u,&V);
flow[u][V+n]=inf;
}
int mx_flow=0,res;
while(bfs(s,t))
{
while(res=dfs(s,inf))
mx_flow+=res;
}
cout<<mx_flow<<endl;
dfs(s);
for(int i=1;i<=n;i++)
{
if(!vis[i])
v.push_back(i);
if(vis[i+n])//说明s->u,u->i+n有流 则i+n->t肯定在最小割上
v.push_back(i+n);
}
cout<<v.size()<<endl;
for(int i=0;i<v.size();i++)
{
if(v[i]<=n)
printf("%d-\n",v[i]);
else
printf("%d+\n",v[i]-n);
}
cout<<endl;
}
return 0;
}
探讨了如何通过最小割模型求解图中所有边的最小点权覆盖问题,介绍了一种有效的算法实现,并提供了详细的代码示例。
396

被折叠的 条评论
为什么被折叠?



