poj 3469 最大流dinic(双核CPU)

题意:有一台双核电脑,给多个任务,每个任务可以在两个核之一运行。分别给出每个任务在第一个核和第二个核上运行的消耗。后面的m行输入是给出两个任务在两个不同核上运行需要付出的额外消耗。问将所有任务做完所需要的最小耗费。

思路:把每个任务作为节点,在超级源点与任务间的连一条边,其容量为给任务在核1上运行的消耗,在该任务节点与超级汇点之间连一条边,容量为该任务在核2上运行的消耗。
在任务之间连接无向边,容量为两个任务不在同一核上运行的额外消耗。则原问题转化为图中的最小割。通过最小割=最大流转化为最大流来求。此题比较卡时间,需要用dinic算法来做,而且其中有一个优化比较惊艳(扫边每次不必重扫,而是记录上次扫到得位置即可)。

#include <cstdio>
#include <cstring>
#define N 20010
#define M 200005
struct edge{
    int y,next,c;
}e[4*(N+M)];
int layer[N],tmp[N],first[N],q[M],stack[N];
int n,m,tt;
void add(int x,int y,int c){
    e[tt].y = y;
    e[tt].c = c;
    e[tt].next = first[x];
    first[x] = tt++;
    e[tt].y = x;
    e[tt].c = 0;
    e[tt].next = first[y];
    first[y] = tt++;
}
int bfs(int s,int t){//建立层次图,分层
    int i,front,rear,now;
    memset(layer, -1, sizeof(layer));
    front = rear = -1;
    q[++rear] = s;
    layer[s] = 0;
    while (front < rear) {
        now = q[++front];
        for(i = first[now];i!=-1;i=e[i].next)
            if(e[i].c && layer[e[i].y]==-1){
                q[++rear] = e[i].y;
                layer[e[i].y] = layer[now]+1;
                if(e[i].y == t)
                    return 1;
            }
    }
    return 0;
}
int dinic(int s,int t){//用临界表,栈中存放的不再是节点序号,而是边在结构体e数组中的序号
    int i,j,k,now,res=0,top=-1;
    while(bfs(s,t)){//如果还能够将图分层,建立层次图
        memcpy(tmp, first, sizeof(first));//建立一个临时数组,因为之后的优化就是基于first数组上面的
        top = -1;
        now = s;
        while(1){
            if (now == t) {             //如果当前增广路径到达了汇点
                int aug = 0x3fffffff;   //增广量
                for(i = 0;i<=top;i++)
                    if(e[stack[i]].c < aug){
                        aug = e[stack[i]].c;
                        k = i;          //用于记录下次搜索的起点
                    }
                res += aug;//更新最大流
                for(i = 0;i<=top;i++){  //更新图中流量和参与网络中的流量
                    e[stack[i]].c -= aug;
                    e[stack[i]^1].c += aug;
                }
                top = k-1;              //新栈顶
                if(top == -1)
                    now = s;
                else
                    now = e[stack[top]].y;
            }
            for(i = tmp[now];i!=-1;i = tmp[now] = e[i].next)//这是优化的关键,边不必再扫一遍,只需从上次扫到的地方继续扫即可
                if(layer[e[i].y]==layer[now]+1 && e[i].c > 0)
                    break;
            if(i!=-1){                  //如果能够增广
                stack[++top] = i;
                now = e[i].y;
            }else{
                if(top == -1)           //再无增广路经
                    break;
                top--;                  //弹出栈顶
                if(top == -1)
                    now = s;
                else                    //取得上一个结点
                    now = e[stack[top]].y;
                tmp[now] = e[tmp[now]].next;//一开始没加这句话导致死循环,debug近一小时
            }
        }
    }
    return res;
}
int main(){
    int i,j,a,b;
    tt = 0;
    memset(first, -1, sizeof(first));
    scanf("%d %d",&n,&m);
    for(i = 1;i<=n;i++){
        scanf("%d %d",&a,&b);
        add(0,i,a);
        add(i,n+1,b);
    }
    for(i = 1;i<=m;i++){
        scanf("%d %d %d",&a,&b,&j);
        add(a,b,j);
        add(b,a,j);
    }
    printf("%d\n",dinic(0,n+1));
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值