思路来源:http://blog.csdn.net/lenleaves/article/details/7873441
求最小点权覆盖,同样求一个最小割,但是要求出割去了那些边,
只要用最终的剩余网络进行一次遍历就可以了,比较简单。
建图:同样是一个二分图,左边的点代表去掉出边,
右边的点代表去掉入边(小心别弄混),左边去掉出边的点与源点相连,
容量为wi- 。
然后更据给出的弧进行连线,权值为INF
使用很好理解的EK算法:(360MS)
//#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include <stdio.h> #include <iostream> #include <climits> #include <cstring> #include <cmath> #include <stack> #include <queue> #include <vector> #include <algorithm> #define mm(a) memset((a),0,sizeof((a))) #define ll long long using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 220; queue <int> que; int vis[MAXN], res[MAXN], pre[MAXN]; int n, m, map[MAXN][MAXN]; int src, des; stack <int> ss; bool bfs(int src, int des){ int index; memset(pre, -1, sizeof(pre)); while(!que.empty()) que.pop(); pre[src] = 0; que.push(src); while(!que.empty()){ index = que.front(); que.pop(); for(int i = src; i <= des; ++i){ if(pre[i] == -1 && map[index][i] > 0){ pre[i] = index; if(i == des) return true; que.push(i); } } } return false; } int MaxFlow(int src, int des){ int i, maxflow = 0; while(bfs(src, des)){ int minflow = INF; for(i = des; i != src; i = pre[i]) minflow = min(minflow, map[pre[i]][i]); for(i = des; i != src; i = pre[i]){ map[pre[i]][i] -= minflow; map[i][pre[i]] += minflow; } maxflow += minflow; } return maxflow; } void init(){ mm(map);mm(vis); } void dfs(int p){ if(vis[p]) return ; vis[p] = true; for(int i = src; i < des; ++i) if(!vis[i] && map[p][i]) dfs(i); } void solve(){ int i, x, y, ans = 0, temp; init(); for(i = 1; i <= n; ++i) scanf("%d",&map[n + i][n * 2 + 1]); for(i = 1; i <= n; ++i) scanf("%d",&map[0][i]); for(i = 1; i <= m; ++i){ scanf("%d%d",&x,&y); map[x][y + n] = INF; } n = n << 1; src = 0, des = n + 1; ans = MaxFlow(src, des); dfs(0); printf("%d\n",ans); n = n >> 1; for(i = 1; i <= n; ++i){ if(!vis[i]) ss.push(i); if(vis[i + n]) ss.push(i + n); } printf("%d\n",ss.size()); while(!ss.empty()){ temp = ss.top(); ss.pop(); if(temp <= n) printf("%d -\n",temp); else printf("%d +\n",temp - n); } } int main(){ while(EOF != scanf("%d%d",&n,&m)) solve(); return 0; }
使用SAP + GAP 优化:(79MS)
//#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include <stdio.h> #include <iostream> #include <climits> #include <cstring> #include <cmath> #include <stack> #include <queue> #include <vector> #include <algorithm> #define mm(a) memset((a),0,sizeof((a))) #define ll long long using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 220; int n, m, map[MAXN][MAXN], dis[MAXN], gap[MAXN]; int src, des; bool vis[MAXN]; stack <int> ss; void init(){ mm(dis);mm(gap);mm(map);mm(vis); } int sap(int u,int flow){ if(u == des) return flow; int ans = 0, i, t; for(i = 0; i <= n + 1; ++i) if(map[u][i] && dis[u] == dis[i] + 1){ t = sap(i, min(flow - ans, map[u][i])); map[u][i] -= t, map[i][u] += t,ans += t; if(ans == flow) return ans; } if(dis[src] >= n + 2) return ans; if(!--gap[dis[u]]) dis[src] = n + 2; ++gap[++dis[u]]; return ans; } void dfs(int p){ if(vis[p]) return ; vis[p] = true; for(int i = 0; i < n + 1; ++i) if(!vis[i] && map[p][i]) dfs(i); } void solve(){ int i, x, y, ans = 0, temp; init(); for(i = 1; i <= n; ++i) scanf("%d",&map[n + i][n * 2 + 1]); for(i = 1; i <= n; ++i) scanf("%d",&map[0][i]); for(i = 1; i <= m; ++i){ scanf("%d%d",&x,&y); map[x][y + n] = INF; } n = n << 1; src = 0, des = n + 1; for(gap[0] = n + 2; dis[src] < n + 2; ) ans += sap(src,INF); dfs(0); printf("%d\n",ans); n = n >> 1; for(i = 1; i <= n; ++i){ if(!vis[i]) ss.push(i); if(vis[i + n]) ss.push(i + n); } printf("%d\n",ss.size()); while(!ss.empty()){ temp = ss.top(); ss.pop(); if(temp <= n) printf("%d -\n",temp); else printf("%d +\n",temp - n); } } int main(){ while(scanf("%d%d",&n,&m)!=EOF) solve(); return 0; }
POJ2125 Destroying The Graph 二分图 + 最小点权覆盖 + 最小割,布布扣,bubuko.com
时间: 2024-10-13 19:53:58