题意简述:给定一个有向图,要通过某些操作删除所有的边,每一次操作可以选择任意一个节点删除由其出发的所有边或者通向它的所有边,两个方向有不同的权值。问最小权值和的解决方案,要输出操作。
乍一看是要用点去覆盖边,联想到二分图的最小点权覆盖,通过拆点,我们可以得到二分图。每个点都拆成两个点,一个作为入点,另一个作为出点。于是我们构建了一个标准的二分图最小点权覆盖的模型
解决二分图最小点权覆盖的的算法并不复杂,创造一个源点和汇点,源点到左边的点连边,容量为对应点的权值,同理右边的点向汇点连边。然后运行最大流,就可以得到最小权值和。
下一步就是输出解决方案,也就是求最小割边集。在运行完最大流的残量网络中由源点出发BFS所有可以到达的点构成了源点集。对于剩下的边,如果满流且一个端点属于源点集而另一个端点不属于,则这条边属于最小割。
代码实现比较简单:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<vector> #include<queue> using namespace std; const int maxn=110*2,maxm=5000,s=0,INF=9999999; int n,m,t,cap[maxn][maxn],flow[maxn][maxn],w[maxn]; int augment(int fa[]) { int d[maxn]; memset(d,0,sizeof(d)); d[s]=INF; queue<int>q;q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int i=s;i<=t;i++) { if((d[i]!=0)||(cap[u][i]<=flow[u][i]))continue; d[i]=min(d[u],cap[u][i]-flow[u][i]); q.push(i); fa[i]=u; if(i==t)return d[t]; } } return d[t]; } int maxflow() { int fa[maxn]; int ans=0; while(true) { int nflow=augment(fa); if(nflow==0)return ans; ans+=nflow; for(int i=t;i!=s;i=fa[i]) { flow[fa[i]][i]+=nflow; flow[i][fa[i]]-=nflow; } } } vector<int> mincut() { bool vis[maxn]; memset(vis,0,sizeof(vis)); vis[s]=true; queue<int>q; q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int i=s;i<=t;i++) { if((vis[i])||(cap[u][i]<=flow[u][i]))continue; vis[i]=true; q.push(i); } } vector<int>ans; for(int i=s;i<=t;i++) if(vis[i])for(int j=s;j<=t;j++) if(!vis[j]&&cap[i][j]==flow[i][j]&&cap[i][j]>0) { if(i==s){ans.push_back(j);} if(j==t){ans.push_back(i);} } return ans; } void print(vector<int> vec) { cout<<vec.size()<<endl; for(int i=0;i<vec.size();i++) { if(vec[i]<=n)cout<<vec[i]<<" -"<<endl; else cout<<vec[i]-n<<" +"<<endl; } } int main() { ios::sync_with_stdio(false); memset(cap,0,sizeof(cap));memset(flow,0,sizeof(flow)); cin>>n>>m; t=n*2+1; for(int i=1;i<=n*2;i++) cin>>w[i]; for(int i=0;i<m;i++) { int a,b; cin>>a>>b; cap[a][b+n]=INF; } for(int i=1;i<=n;i++) { cap[s][i]=w[i+n]; cap[i+n][t]=w[i]; } cout<<maxflow()<<endl; print( mincut() ); return 0; }
东北欧赛区的题目还是很给力的