http://poj.org/problem?id=3469
题意:要在双核AB上面运行N个模块,模块i在Acpu上处理的花费为A,在Bcpu上处理的花费为B。同时有m个模块组合(a,b),如果这两个模块不在同一个cpu中处理就会产生额外的花费w。请计算处理完这些任务的最小花费。
ps:感觉今天脑子瓦特了。。
思路:第一次接触和费用有关的网络流,虽然对费用流还不了解,不过知道这题就是最小割,第一道最小割吧。所谓最小割,就是在流向图上去掉数量最少容量最小的边,使这个图变得不连通,源点s无法到达汇点t,这些边组成的容量就是最小割。直观一想,我们知道这些割边一定是组成最大流所有基础增广路中的一条边,加一点想象力嘛,最大流的来源就是这些小溪,小溪都干涸了,最大流也就不存在了。而我们知道连通的边一定有流量,所以所有小溪都干涸也就意味着不连通,割的目的也就达到了。至于掐断基础增广路中的哪条边,那肯定是代表性的一条边呗(可以代表整条增广路的流量),非满流那条莫属了。那么至此,掐断满流边(割边)就是掐断基础增广路,掐断所有基础增广路就变得不连通,那么这些满流边(割边)的容量和就是最小割。而满流边所构成的基础增广路合在一起又变成最大流,所以最小割==最大流。(还是感觉有点绕= =)
这题中模块要么被A处理要么被B处理,每条增广路只能有单方向的一条边可以连,得到的结果图必然为非连通,于是建立起A(源点)->模块(顶点)->B(汇点)[A->模块容量为A,模块->B容量为B]的图。实质上就是在这个图上割去哪些边,使得在割这些边花费最小的前提下将图变成非连通图,这样就变成了最小割问题。至于割哪些边,就是可以代表每条增广路的那些满流边啦~上面也得出最小割==最大流,所以直接求源点到汇点的最大流即可。
如果说只有上面这些还好,我觉得最麻烦的是添加节点2-节点3这条双向边,也就是不在同一个cpu处理的额外花费。这个边权值在最小割里又如何理解?上图吧:(题目中的样例)
从这幅图里可以看出,如果不加这条双向边,最小花费即为1+2+3=6。加了这条边后,原先s->2->t的路变成了s->2->3->t,原先s->3->t的路变成了s->3->2->t。原先容量2接10变成2接3,10接3变成10接10,道路的利用率增加了,这样最小花费变成1+2+10=13。再来看,原先割的边是s->1、s->2、3->t,现在第三条边变成了s->3。那么加边起了什么作用呢?让整个图更不容易变为非连通。原本割一条容量3的边就行,现在因为多了这条边,割了依然连通。而这条边因为容量太大,割不动啊,只能去割容量为10的边。看吧,实际应用中的由于不在同一个cpu所产生的额外费用这里转化为一条割不动的边,要想不支付这种巨额费用,就去找别的边啊。注意这里把2和3连在一起后,实际上就产生了一种2在s、3在t同时发生的情况,正好对应题中的情况。好好想想这边的意义,以后建图我们也就好想一些。
整体而言和普通最大流思维不一样,问题转化就是建图的基础,看清事物的本质是做出图论题的关键。
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 20005;
const int INF = 0x3f3f3f3f;
int head[N], dis[N], cur[N];
bool vis[N];
int n, m, cnt;
struct Edge
{
int to, cap, flow, next;
}edge[N*50];
void add(int u, int v, int w)
{
edge[cnt] = (struct Edge){v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = (struct Edge){u, 0, 0, head[v]};
head[v] = cnt++;
}
bool bfs(int start, int endd)
{
memset(dis, -1, sizeof(dis));
memset(vis, false, sizeof(vis));
queue<int>que;
dis[start] = 0;
vis[start] = true;
que.push(start);
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(!vis[E.to] && E.flow<E.cap)
{
dis[E.to] = dis[u]+1;
vis[E.to] = true;
if(E.to == endd) return true;
que.push(E.to);
}
}
}
return false;
}
int dfs(int x, int res, int endd)
{
if(x == endd || res == 0) return res;
int flow = 0, f;
for(int& i = cur[x]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dis[E.to] == dis[x]+1)
{
f = dfs(E.to, min(res, E.cap-E.flow), endd);
if(f>0)
{
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
res -= f;
if(res == 0) break;
}
}
}
return flow;
}
int max_flow(int start, int endd)
{
int flow = 0;
while(bfs(start, endd))
{
memcpy(cur, head, sizeof(head));
flow += dfs(start, INF, endd);
}
return flow;
}
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void getmap()
{
int a, b, w;
int A, B;
for(int i = 1; i <= n; i++)
{
scanf("%d%d", &A, &B);
add(0, i, A);
add(i, n+1, B);
}
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &a, &b, &w);
add(a, b, w);
add(b, a, w);
}
}
int main()
{
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &m);
init();
getmap();
int ans = max_flow(0, n+1);
printf("%d\n", ans);
return 0;
}