hdu 5294 Tricks Device(15多校第一场1007)(spfa+最小割)

//最小割+spfa
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
#define MIN(a,b) a<b?a:b
#define MAX(a,b) a>b?a:b
const int N=50000,M=500000,INF=0x3fffffff;

int n,m;
struct EDGE{
    int from,to,cap,next;
}edge[M],edge2[M];
int head[N];
int cnt;
void addedge(int u,int v,int w){
    edge[cnt].from=u;edge[cnt].to=v;edge[cnt].cap=w;edge[cnt].next=head[u];
    head[u]=cnt++;
}
void addedge2(int u,int v,int w){
    edge2[cnt].from=u;edge2[cnt].to=v;edge2[cnt].cap=w;edge2[cnt].next=head[u];
    head[u]=cnt++;
}
int dist[N][2];
int step[N];
int vis[N];
void SPFA(int st,int num){
    for(int i=0;i<=n;i++)dist[i][num]=INF,step[i]=INF;
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(st);
    dist[st][num]=0;
    step[st]=0;vis[st]=1;
    while(!q.empty()){
        int k=q.front();
        q.pop();vis[k]=0;
        for(int i=head[k];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(dist[k][num]+edge[i].cap<dist[v][num]){
                dist[v][num]=dist[k][num]+edge[i].cap;
                step[v]=step[k]+1;
                if(!vis[v]){
                    q.push(v);
                    vis[v]=1;
                }
            }
            else if(dist[k][num]+edge[i].cap==dist[v][num]){
                step[v]=MIN(step[v],step[k]+1);
            }
        }
    }
}
int q[N];
int dep[N];//dep为距离编号
int gap[N];//gap[x]=y:说明残留网络中dep[i]==x的节点有y个

struct SAP{
    int sum;
    int src,des;
    void init(){
        src = 1;
        des = n;
    }
    void bfs(int start,int end){
        memset(dep, -1, sizeof(dep));
        memset(gap, 0, sizeof(gap));
        memset(q,-1,sizeof(q));
        gap[0]=1;
        int front,rear;
        front=rear=0;
        dep[end]=0;
        q[rear++]=end;
        while(front!=rear){
            int u=q[front++];
            if(front==N)front=0;
            for(int i=head[u];i!=-1;i=edge2[i].next){
                int v=edge2[i].to;
                if(dep[v]!=-1)continue;
                q[rear++]=v;
                if(rear==N)rear=0;
                dep[v]=dep[u]+1;
                ++gap[dep[v]];
            }
        }
    }
    int Maxflow(){
        int res=0;
        bfs(src,des);
        int cur[N],stack[N];
        int top=0;
        memcpy(cur,head,sizeof(head));
        int u=src;
        int i;
        while(dep[src]<n){//如果src找不到一个可达点,则dep[src] = n + 1自动退出
            if(u==des){//找到汇点
                int temp=INF,inser=n;
                for(int i=0;i<top;i++){//找从src点到des点的所有边中的最容量为temp,而那条边的编号为insert
                    if(temp>edge2[stack[i]].cap){
                        temp=edge2[stack[i]].cap;
                        inser=i;
                    }
                }
                for(int i=0;i<top;i++){//将正向边-temp,反向变+temp
                    edge2[stack[i]].cap-=temp;
                    edge2[stack[i]^1].cap+=temp;
                }
                res+=temp;//总的流量加temp
                top=inser;//stack只保留从src到最小边insert“前向点”之间的边的信息,即insert边以后的边信息都不要
                u=edge2[stack[top]].from;u为insert的”前向点“
            }
            
            for(i=cur[u];i!=-1;i=edge2[i].next){当没有断层,找出一个可达边
                if(edge2[i].cap!=0&&dep[u]==dep[edge2[i].to]+1)break;
            }
            if(i!=-1)//找到这个可达边,只能走到dep[u]-1;
            {
                cur[u]=i;//优化下次找dep[u] - 1不用从头开始找了
                stack[top++]=i;
                u=edge2[i].to;
            }
            else{//当没有找到深度为dep[u] - 1的可达边,那只能找深度更大的边
                if(--gap[dep[u]]==0)break;   //出现断层,无增广路;
                int minn = n;             //从头开始找出深度最小的可达边
                for(i=head[u];i!=-1;i=edge2[i].next){
                    if(edge2[i].cap==0)continue;
                    if(minn>dep[edge2[i].to]){
                        minn=dep[edge2[i].to];
                        cur[u]=i;
                    }
                }
                dep[u]=minn+1;//更新深度
                ++gap[dep[u]];
                if(u!=src)//如果u不是源点,还得回溯到dep[u] + 1(这里的dep[u]!=minn + 1)层,并将dep[u]层点全部修改变大。一直回溯到源点
                    u=edge2[stack[--top]].from;
            }
        }
        return res;
    }
};
SAP sap;

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        cnt=0;
        memset(head,-1,sizeof(head));
        
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        SPFA(1,0);
        int ans=step[n];
        SPFA(n,1);
        int len=dist[n][0];
        
        cnt=0;
        memset(head,-1,sizeof(head));
        
        for(int i=0;i<2*m;i+=2){//重新建图
            int u=edge[i].from,v=edge[i].to;
            if(dist[u][0]+edge[i].cap+dist[v][1]==len){
                addedge2(u,v,1);
                addedge2(v,u,0);
            }
            else if(dist[u][1]+edge[i].cap+dist[v][0]==len){
                addedge2(v,u,1);
                addedge2(u,v,0);
            }
        }
        sap.init();
        printf("%d %d\n",sap.Maxflow(),m-ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值