poj 3204 Ikki‘s Story I(最大流-关键边)

Ikki’s Story I - Road Reconstruction
题意:找出图中“关键边”的条数,“关键边”:增加这条边的流量,源点s到汇点t的最大流量将增加。

思路:因为关键边一定是满流边(如果最开始都没用完,再增加这条边的流量肯定也用不上),所以我们跑完网络最大流后,直接判断这些满流边(即残量为0的边)是否满足:①源点s能否到达这条边的起点;②这条边的终点能否到达汇点t。满足这两点时,增加这条边的流量,最大流就肯定会增加。
注意:满流边不一定是关键边,因为可能会出现一条路径上有几条满流边的情况,这时候增加其中一条边的流量,这条路径上的最大流不会增加。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<iomanip>
using namespace std;
typedef long long LL;
const int manx=1e4+1e3;
const int manx2=1e7+10;
const int mod=1e9+7;
const int INF=1e9;
const double inf=1e9+7;

int head[600],cur[600],deep[600],vs1[600],vs2[600];
int n,m,cou;//vs1记录源点s能到达的点//vs2记录能到达汇点t的点
int s=0,t;
struct node
{
    int s,e,w,bf;
} edge[manx];
void add(int s,int e,int w)
{
    edge[cou]=node{s,e,w,head[s]};
    head[s]=cou++;
}
void init()
{
    cou=0;
    memset(head,-1,sizeof head);
    memset(vs1,0,sizeof vs1);
    memset(vs2,0,sizeof vs2);
}
int bfs()
{
    memcpy(cur,head,sizeof cur);
    memset(deep,-1,sizeof deep);
    queue<int>qu;
    qu.push(s);
    deep[s]=0;
    while(!qu.empty())
    {
        int now=qu.front();
        qu.pop();
        for(int i=head[now]; ~i; i=edge[i].bf)
        {
            int e=edge[i].e;
            if(deep[e]==-1&&edge[i].w>0)
            {
                deep[e]=deep[now]+1;
                qu.push(e);
            }
        }
    }
    return deep[t]!=-1;
}
int dfs(int now=s,int flow=INF)
{
    if(now==t)
        return flow;
    int ans=0;
    for(int i=cur[now]; ~i; i=edge[i].bf)
    {
        cur[now]=i;
        int e=edge[i].e;
        if(edge[i].w>0&&deep[e]==deep[now]+1)
        {
            int temp=dfs(e,min(flow,edge[i].w));
            flow-=temp,ans+=temp;
            edge[i].w-=temp;
            edge[i^1].w+=temp;
            if(!flow)
                break;
        }
    }
    return ans;
}
int Dinic()
{
    int ans=0;
    while(bfs())
        ans+=dfs();
    return ans;
}
void dfs1()
{
    queue<int>qu;
    qu.push(s);
    vs1[s]=1;
    while(!qu.empty())
    {
        int now=qu.front();
        qu.pop();
        for(int i=head[now];~i;i=edge[i].bf)
        {
            if(!vs1[edge[i].e]&&edge[i].w>0)
            {
                vs1[edge[i].e]=1;
                qu.push(edge[i].e);
            }
        }
    }
}
void dfs2()
{
    queue<int>qu;
    qu.push(t);
    vs2[t]=1;
    while(!qu.empty())
    {
        int now=qu.front();
        qu.pop();
        for(int i=head[now];~i;i=edge[i].bf)
        {
            if(!vs2[edge[i].e]&&edge[i^1].w>0)
            {//这里和dfs1有一点不同,
             //因为这里是看哪些点能到汇点t,所以要看反向边 i^1
                vs2[edge[i].e]=1;
                qu.push(edge[i].e);
            }
        }
    }
}
int slove()
{
    dfs1();
    dfs2();
    int ans=0;
    for(int i=0;i<=m*2-1;i+=2)
        if(edge[i].w==0&&vs1[edge[i].s]&&vs2[edge[i].e])
            ans++;
    return ans;

}
int main()
{
    init();
    int x,y,w,temp=0;
    scanf("%d%d",&n,&m);
    t=n-1;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d%d",&x,&y,&w);
        if(w==0)
        {
            temp++;
            continue;
        }
        add(x,y,w);
        add(y,x,0);
    }
    m-=temp;//不知道有没有流量为0的边,就判断了一下
    Dinic();
    printf("%d\n",slove());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值