POJ 3469 第一次AC总结(Dinic+多路增广)

这题是道神题,神就神在,它既能让你搞懂网络流及其优化,还给了你很大的优化空间。首先,说一下算法,这题就是一道最小割+简单构图的题目,增加两个节点分别表示两个CPU,第i个界点与两个CPU各连接一条,边权值分别为Ai、Bi,在后面的输入中要分别给ab和ba都连接一条权值为w的边(注意不要再连反向边了),然后用你建的图求最大流即可,这回我用的是Dinic求解。

下面便是优化,这题的优化最有学问,这次我只用了一个优化——多路增广,由于我的水平低,所以我只能讲个大概:多路增广如其名,即是用一次增广代替了多次增广,到一个节点时就一次性地把与其相邻的所有边翻出来求增广的和,如果和为零,代表它已增广不出什么了,把这个点废掉即可。这样就省去了冗余的增广。再次提醒,某超级大神牛说过,“废掉”这句话及其重要,一定不能省。

这次我用了4594MS,希望下次能分享到更多优化技巧,使我的程序跑得更快!

附属代码(第一次用CSDN,据说CSDN的代码着色挺专业的,试用一下):

#include <cstdio>
#include <string.h>
#include <queue>
#include <algorithm>
#include <utility>
using namespace std;
const int NMax=20020,MeMax=500000;
struct edge
{
    int num,len;
    edge *next,*rev;
}*S[NMax];
int N,M,L,level[NMax];
edge Me[MeMax];
queue<int> Q;
void Build(int x,int y,int z)
{
    edge *tmp,*tmp2;
    tmp=&Me[L++];
    tmp->num=y;
    tmp->len=z;
    tmp->next=S[x];
    tmp2=&Me[L++];
    tmp2->num=x;
    tmp2->len=0;
    tmp2->next=S[y];
    tmp->rev=tmp2;
    tmp2->rev=tmp;
    S[x]=tmp;
    S[y]=tmp2;
}
void Build2(int x,int y,int z)
{
    edge *tmp,*tmp2;
    tmp=&Me[L++];
    tmp->num=y;
    tmp->len=z;
    tmp->next=S[x];
    tmp->rev=NULL;
    S[x]=tmp;
}
bool CCT()
{
    while(!Q.empty()) Q.pop();
    int tmp;
    memset(level,-1,sizeof(level));
    level[0]=0;
    Q.push(0);
    while(!Q.empty())
    {
        tmp=Q.front();
        Q.pop();
        for(edge *p=S[tmp];p;p=p->next)
        {
            if(p->len && level[p->num]==-1)
            {
                level[p->num]=level[tmp]+1;
                Q.push(p->num);
            }
        }
    }
    return level[N+1]!=-1;
}
int DFS(int a,int Min)
{
    int tmp,tot=0;
    if(a==N+1) return Min;
    for(edge *p=S[a];p;p=p->next)
    {
        if(p->len && level[p->num]==level[a]+1 && tot<Min)
        {
            if(tmp=DFS(p->num,min(p->len,Min-tot)))
            {
                tot+=tmp;
                p->len-=tmp;
                if(p->rev!=NULL) p->rev->len+=tmp;
            }
        }
    }
    if(!tot) level[a]=-1;
    return tot;
}
int main()
{
    int tmp,ans;
    int x,y,z;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        memset(S,0,sizeof(S));
        ans=0;
        L=0;
        for(int i=1;i<=N;i++)
        {
            scanf("%d%d",&x,&y);
            Build(0,i,x);
            Build(i,N+1,y);
        }
        for(int i=1;i<=M;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            Build2(x,y,z);
            Build2(y,x,z);
        }
        while(CCT())
        {
            while(tmp=DFS(0,(~0u>>1)))ans+=tmp;
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值