Time Limit: 2000MS | Memory Limit: 65536K | |||
Total Submissions: 7597 | Accepted: 2434 | Special Judge |
Description
Alice assigns two costs to each vertex: Wi + and Wi -. If Bob removes all arcs incoming into the i-th vertex he pays Wi + dollars to Alice, and if he removes outgoing arcs he pays Wi - dollars.
Find out what minimal sum Bob needs to remove all arcs from the graph.
Input
Output
Sample Input
3 6 1 2 3 4 2 1 1 2 1 1 3 2 1 2 3 1 2 3
Sample Output
5 3 1 + 2 - 2 +
题意:给你一幅有向图, 对于点i删除所有进入该点的边就要支付费用W[i]+(情况1), 删除所有从该点出发的边就要支付费用W[i]-,问删除图中的所有边至少需要多少费用(情况2)。
分析:首先我们根据题意,选点就能删除一些边, 那么这可以看成是“用点去覆盖边”, 这里无非是把边分成了2类,
我们可以把原来的点进行拆点,那么就完完全全等价于“用点去覆盖边",如果支付费用都为1,那么这就是”最小点覆盖集“问题,但这题费用不确定,那么这就是“最小点权覆盖集”问题, 借助二分匹配的思想,我们可以引入“最小割”来解决“最小点权覆盖”问题。
建图:拆点,左点阵为情况2的点, 右点阵为情况1的点,右点阵跟汇点T连流量为W+,左点阵跟源点S连费用W-,
对于输入的边<u, v> 连边 (u, v+n)费用为无穷大inf。跑一边最大流,求出最小费用。
输出解:不理解啊。
void solve(int u){
mark[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(edge[i].cap - edge[i].flow > 0 && !mark[v])
solve(v);
}
}
总结:二分匹配的题都可以用最大流来解,在二分图中 有 “最小点覆盖集”和“最打独立集”,如果有了点权,那么就要用最大流(最小割)来解决 “最小点权覆盖集”(最小割)和“最大点权独立集”(最大流)问题。
以上解析来自http://blog.csdn.net/pi9nc/article/details/27112091
看了别人的解析一下午,对于最小割集的那一块还是不懂, 其他大神还都是用sap写的,sap输出解集和dinic的输出解集的代码还不一样, 而且对最小割不够理解,只能一点一点的调试,试了一下午终于试了出。醉了醉了,理论不扎实,只会用模板的诟病。奋斗奋斗,要理解最大流和最小割 。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
#define maxn 5500
#define maxm 1000000
#define INF 0x3f3f3f3f
using namespace std;
int m, n;
int head[maxn],cur[maxn], cnt;
int dist[maxn], vis[maxn];
int mark[maxn];
struct node{
int u, v, cap, flow, next;
};
node edge[maxm];
void init(){
cnt = 0;
memset(head, -1, sizeof(head));
memset(mark, 0, sizeof(mark));
}
void add(int u, int v, int w){
node E1 = {u, v, w, 0, head[u]};
edge[cnt] = E1;
head[u] = cnt++;
node E2 = {v, u, 0, 0, head[v]};
edge[cnt] = E2;
head[v] = cnt++;
}
void getmap(){
int a, u ,v;
for(int i = 1; i <= n; ++i){
scanf("%d", &a);
add(i + n, n * 2 + 1, a);
}
for(int i = 1; i <= n; ++i){
scanf("%d", &a);
add(0, i, a);
}
while(m--){
scanf("%d%d", &u, &v);
add(u, v + n, INF);
}
}
bool BFS(int st ,int ed){
queue<int>q;
memset(vis, 0 ,sizeof(vis));
memset(dist, -1, sizeof(dist));
vis[st] = 1;
dist[st] = 0;
q.push(st);
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = head[u]; i != -1; i = edge[i].next){
node E = edge[i];
if(!vis[E.v] && E.cap > E.flow){
vis[E.v] = 1;
dist[E.v] = dist[u] + 1;
if(E.v == ed)
return true;
q.push(E.v);
}
}
}
return false;
}
int DFS(int x, int ed, int a){
if(x == ed || a == 0)
return a;
int flow = 0, f;
for(int &i = cur[x]; i != -1; i = edge[i].next){
node &E = edge[i];
if(dist[E.v] == dist[x] + 1 && (f = DFS(E.v, ed, min(a, E.cap - E.flow))) > 0){
E.flow += f;
edge[i ^ 1].flow -= f;
a -= f;
flow += f;
if(a == 0)
break;
}
}
return flow;
}
int maxflow(int st, int ed){
int flowsum = 0;
while(BFS(st,ed)){
memcpy(cur, head, sizeof(head));
flowsum += DFS(st, ed, INF);
}
return flowsum;
}
void solve(int u){
mark[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
int v = edge[i].v;
if(edge[i].cap - edge[i].flow > 0 && !mark[v])
solve(v);
}
}
int main (){
while(scanf("%d%d", &n, &m) != EOF){
init();
getmap();
printf("%d\n", maxflow(0, 2 * n + 1));
solve(0);
int ans= 0;
for(int i = 1; i <= n; ++i){
if(!mark[i]) ans++;
if(mark[i + n]) ans++;
}
printf("%d\n", ans);
for(int i = 1;i <= n; ++i){
if(!mark[i]) printf("%d -\n", i);
if(mark[i + n]) printf("%d +\n", i);
}
}
return 0;
}