poj3469 Dual Core CPU(最大点权独立集+最小割+理解)


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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值