Poj 3204 Ikki's Story I - Road Reconstruction【最大流Dinic+可行方案边数统计】

Ikki's Story I - Road Reconstruction
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 7546 Accepted: 2185

Description

Ikki is the king of a small country – Phoenix, Phoenix is so small that there is only one city that is responsible for the production of daily goods, and uses the road network to transport the goods to the capital. Ikki finds that the biggest problem in the country is that transportation speed is too slow.

Since Ikki was an ACM/ICPC contestant before, he realized that this, indeed, is a maximum flow problem. He coded a maximum flow program and found the answer. Not satisfied with the current status of the transportation speed, he wants to increase the transportation ability of the nation. The method is relatively simple, Ikki will reconstruct some roads in this transportation network, to make those roads afford higher capacity in transportation. But unfortunately, the country of Phoenix is not so rich in GDP that there is only enough money to rebuild one road. Ikki wants to find such roads that if reconstructed, the total capacity of transportation will increase.

He thought this problem for a loooong time but cannot get it. So he gave this problem to frkstyc, who put it in this POJ Monthly contest for you to solve. Can you solve it for Ikki?

Input

The input contains exactly one test case.

The first line of the test case contains two integers NM (N ≤ 500, M ≤ 5,000) which represents the number of cities and roads in the country, Phoenix, respectively.

M lines follow, each line contains three integers abc, which means that there is a road from city a to city b with a transportation capacity of c (0 ≤ ab < nc ≤ 100). All the roads are directed.

Cities are numbered from 0 to n − 1, the city which can product goods is numbered 0, and the capital is numbered n − 1.

Output

You should output one line consisting of only one integer  K, denoting that there are  K roads, reconstructing each of which will increase the network transportation capacity.

Sample Input

2 1

0 1 1

Sample Output

1

Source


题目大意:

有n个点,m条有向边,0号是生产站点,N-1是接受站点,每条边都有限定的流量,Ikki知道这是一个最大流问题,他求出了其网络的最大流,但是他发现这个网络的最大流值很小,没有运输的效率,他想增加这个值,然而增加这个值的方法也很简单,就是他需要对一些边进行增加容量。然而政府比较穷,他只能增加一条边的容量,问有多少个这样的边。


思路:


1、首先分析,对于残余网络中(不算退回边),瓶颈边的剩余流量一定是0,也就是一条满流边、


2、那么对于剩余流量为0的边,是不是加上容量就一定可行呢?

我们来看这样一个例子:


明显每条边的剩余流量都是0,而且我对其在原图任意一条边增加容量都不能达到使最大流值增加的情况。


3、那么我们到底怎样加才行呢?

我们再来看这样一个例子


剩余流量为:


显然这种情况下,对这条剩余流量为0的边加流量,是一定会达到增加最大流值的目的的。


4、那么我们仔细观察上图,不难理解:对于一条剩余流量为0的边,(在走流量大于0的边的情况下)如果从源点能够到达这条边的from,并且这条边的to能够到达汇点。那么这条边就是一条可行边。


5、那么整体思路就是:跑一遍Dinic,得到残余网络,对于每一条剩余流量为0的边,进行上述判断,并记录下可行边数量即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
    int from;
    int to;
    int next;
    int w;
}e[50550];
int vis[5050];
int cur[5050];
int head[5050];
int divv[5050];
int n,m,cont,ss,tt;
void add(int from,int to,int w)
{
    e[cont].from=from;
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
int makedivv()
{
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(tt==u)return maxflow;
    int ret=0;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
void Dinic()
{
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        Dfs(ss,INF,tt);
        ans++;
    }
    //printf("%d\n",ans);
}
int findd(int start,int end)
{
    memset(vis,0,sizeof(vis));
    queue<int >s;
    s.push(start);
    vis[start]=1;
    while(!s.empty())
    {
        int u=s.front();
        if(u==end)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w)
            {
                if(vis[v]==0)
                {
                    s.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    return 0;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        ss=1;
        tt=n;
        cont=0;
        cont++;
        cont--;
        cont++;
        cont--;
        cont++;
        cont--;
        cont++;
        cont--;
        cont++;
        cont--;//你打我啊~
        memset(head,-1,sizeof(head));
        for(int i=0;i<m;i++)
        {
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            x++;y++;
            add(x,y,w);
            add(y,x,0);
        }
        Dinic();
        int output=0;
        for(int i=0;i<cont;i+=2)
        {
            if(e[i].w!=0)continue;
            if(findd(ss,e[i].from)==1&&findd(e[i].to,tt)==1)output++;
        }
        printf("%d\n",output);
    }
}
/*
4 6
0 1 4
0 2 2
1 2 2
1 3 1
2 3 7
2 0 1
*/



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值