POJ 2125 最小点权覆盖集(输出方案)
题意:给一个图(有自回路,重边),要去掉所有边,规则:对某个点,可以有2种操作:去掉进入该点
的所有边,也可以去掉出该点所有边,(第一种代价为w+,第二种代价为w-)。求最小代价去除所有边。
己思:点的权被分为出入?必然拆点啊!每个点一分为二,点权:出的为出点权,入的为入点权,原来边仍在
,注意,这里(1-)->(1+),不多添加边,因为相当于求新图的最小点权覆盖集(覆盖所有边,每选一个点
恰好去除了它的边(要原图的入边或出边),),所以不可以在新图上再添加边(这里不是到不到的问题了)
,若添加边i-->i+,最小割可能发生变化。所以求二分图最小割问题,=新添s,t的最大流。
这题要输出方案,即最小点权覆盖集的点,即最小割(该二分图为简单割),遍历边,若该边俩头
的所有边,也可以去掉出该点所有边,(第一种代价为w+,第二种代价为w-)。求最小代价去除所有边。
己思:点的权被分为出入?必然拆点啊!每个点一分为二,点权:出的为出点权,入的为入点权,原来边仍在
,注意,这里(1-)->(1+),不多添加边,因为相当于求新图的最小点权覆盖集(覆盖所有边,每选一个点
恰好去除了它的边(要原图的入边或出边),),所以不可以在新图上再添加边(这里不是到不到的问题了)
,若添加边i-->i+,最小割可能发生变化。所以求二分图最小割问题,=新添s,t的最大流。
这题要输出方案,即最小点权覆盖集的点,即最小割(该二分图为简单割),遍历边,若该边俩头
vis[]值(dinic后)不同,(i%2==0)必然为最小割边。
ps:神题??
#include<iostream> //110ms ,1A #include<queue> #include<cstdio> #include<vector> using namespace std; int n,m; int e[30000][3];int head[208];int nume=0; const int inf=0x3f3f3f3f; void addedge(int f,int l,int w) { e[nume][0]=l;e[nume][1]=head[f];head[f]=nume; e[nume++][2]=w; e[nume][0]=f;e[nume][1]=head[l];head[l]=nume; e[nume++][2]=0; } int vis[205];int level[205]; bool bfs() { for(int i=0;i<=2*n+1;i++) vis[i]=level[i]=0; vis[0]=1;queue<int>q;q.push(0); while(!q.empty()) { int cur=q.front();q.pop(); for(int i=head[cur];i!=-1;i=e[i][1]) { int v=e[i][0]; if(!vis[v]&&e[i][2]>0) { level[v]=level[cur]+1; if(v==n*2+1)return 1; vis[v]=1; q.push(v); } } } return vis[n*2+1]; } int dfs(int u,int minf) { if(minf==0||u==n*2+1)return minf; int sumf=0,f; for(int i=head[u];i!=-1&&minf;i=e[i][1]) { int v=e[i][0]; if(level[v]==level[u]+1&&e[i][2]>0) { f=dfs(v,minf<e[i][2]?minf:e[i][2]); e[i][2]-=f;e[i^1][2]+=f; sumf+=f;minf-=f; } } return sumf; } int dinic() { int sum=0; while(bfs()) sum+=dfs(0,inf); return sum; } int main() { scanf("%d%d",&n,&m); //建图 int temp,ff,ll; nume=0; for(int i=0;i<n*2+1;i++) head[i]=-1; for(int i=1;i<=n;i++) { scanf("%d",&temp); addedge(i+n,n*2+1,temp); } for(int i=1;i<=n;i++) { scanf("%d",&temp); addedge(0,i,temp); } for(int i=1;i<=m;i++) { scanf("%d%d",&ff,&ll); addedge(ff,ll+n,inf); } int ans=dinic(); printf("%d ",ans); int count=0; vector<int>cut1,cut2; // 找方案, for(int i=0;i<=2*n+1;i++) for(int j=head[i];j!=-1;j=e[j][1]) { if(j%2==0&&e[j][2]==0&&vis[i]==1&&vis[e[j][0]]==0) //最小割边 { count++; if(i==0) cut1.push_back(e[j][0]); else if(e[j][0]==2*n+1) cut2.push_back(i-n); } } printf("%d ",count); for(int i=0;i<cut1.size();i++) printf("%d - ",cut1[i]); for(int i=0;i<cut2.size();i++) printf("%d + ",cut2[i]); return 0; }