POJ 3204 Ikki's Story I - Road Reconstruction (求割边)

在小国凤凰中,Ikki发现交通速度慢是最大的问题。通过最大流算法,他找到了提升运输能力的方法:重建部分道路以提高其运输容量。本篇详细介绍了如何通过算法找出关键的道路进行改造。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ikki's Story I - Road Reconstruction
Time Limit: 2000MSMemory Limit: 131072K
Total Submissions: 6106Accepted: 1729

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 N, M (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 a, b, c, which means that there is a road from city a to city b with a transportation capacity of c (0 ≤ a, b < n, c ≤ 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
求有多少条割边。割边的意思是,求得最大流以后的残留网络中,假定某条边两个端点为s,e,满足流量为0,且源点到s的过程中流量>0,e到汇点的过程流量>0的边。其实流量为0的意思也就是满流。那么源点到s和汇点到e的流量大于0即是还未满流。对于这道题,如果能够增大s-e的容量,自然就能够继续增广了。
所以此题的做法就是跑一遍最大流,得到一个残留网络。枚举每一条边,如果该边未满流(大于0),则记录该边是连通的。然后从源点dfs找到源点可达的所有点,标记起来。然后再枚举每一条反向边,如果未满流就连通,从汇点开始dfs标记汇点可达的点。最后再枚举每一条边,如果边满流(容量为0),且源点可达该边的起点,汇点可达该边的终点,则这条边就是割边,统计全部这样的边就是答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define SIZE 512
#define inf 0xfffffff

using namespace std;

struct node
{
    int to,val,next;
}edge[SIZE*SIZE];

int N,M,sc,sk,pt;
int head[SIZE],idx;
int gap[SIZE],dis[SIZE];
bool map[SIZE][SIZE],reach[SIZE],antiReach[SIZE];

void addnode(int from,int to,int val)
{
    edge[idx].to = to;
    edge[idx].val = val;
    edge[idx].next = head[from];
    head[from] = idx ++;
    edge[idx].to = from;
    edge[idx].val = 0;
    edge[idx].next = head[to];
    head[to] = idx ++;
}

int dfs(int cur,int cval)
{
    if(cur == sk)
        return cval;
    int mindis = pt - 1, tval = cval;
    for(int i=head[cur]; i!=-1; i=edge[i].next)
    {
        int to = edge[i].to;
        if(edge[i].val > 0)
        {
            if(dis[to] + 1 == dis[cur])
            {
                int val = dfs(to,min(edge[i].val,tval));
                tval -= val;
                edge[i].val -= val;
                edge[i^1].val += val;
                if(dis[sc] >= pt)
                    return cval - tval;
                if(!tval)
                    break;
            }
            if(dis[to] < mindis)
                mindis = dis[to];
        }
    }
    if(cval == tval)
    {
        --gap[dis[cur]];
        if(!gap[dis[cur]])
            dis[sc] = pt;
        dis[cur] = mindis + 1;
        ++gap[dis[cur]];
    }
    return cval - tval;
}

void sap()
{
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    gap[sc] = pt;
    int ret = 0;
    while(dis[sc] < pt)
        ret += dfs(sc,inf);
}

void read()
{
    sc = 0, sk = N-1, pt = sk+1;
    idx = 0;
    memset(head,-1,sizeof(head));
    int s,e,v;
    for(int i=1; i<=M; i++)
    {
        scanf("%d%d%d",&s,&e,&v);
        addnode(s,e,v);
    }
}

void dfs_reach(int v)
{
    reach[v] = true;
    for(int i=0; i<N; i++)
        if(!reach[i] && map[v][i])
            dfs_reach(i);
}

void dfs_antiReach(int v)
{
    antiReach[v] = true;
    for(int i=0; i<N; i++)
        if(!antiReach[i] && map[v][i])
            dfs_antiReach(i);
}

void work()
{
    memset(reach,0,sizeof(reach));
    memset(antiReach,0,sizeof(antiReach));
    memset(map,0,sizeof(map));
    for(int i=0; i<idx; i+=2) //枚举源点未满流的边,记录正向边。其中edge[i^1].to = from;
        if(edge[i].val != 0)
            map[edge[i^1].to][edge[i].to] = true;
    dfs_reach(0);
    memset(map,0,sizeof(map));
    for(int i=0; i<idx; i+=2) //记录反向边
        if(edge[i].val != 0)
            map[edge[i].to][edge[i^1].to] = true;
    dfs_antiReach(N-1);
    int ans = 0;
    for(int i=0; i<idx; i+=2)
        if(edge[i].val == 0 && reach[edge[i^1].to] && antiReach[edge[i].to])
            ans ++;
    printf("%d\n",ans);
}

int main()
{
    scanf("%d%d",&N,&M);
    read();
    sap();
    work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值