中南OJ 2012年8月月赛 B题 Barricade

  中南大学OJ 2012年8月月赛,B题,Barricade题目链接)。

Problem B: Barricade

Description

  GBQC国一共有N个城市,标号分别为1, 2, …, N。N个城市间一共有M条单向通行的道路。

  不幸的是,GBQC国的城市1连续暴雨,使得整个城市淹没在汪洋洪水中,于是GBQC国领导人小明决定让城市1的居民暂时移居到城市N,于是一场浩浩荡荡的搬迁运动开始了。

  但还有一个问题需要解决,居民从城市1出发,如果走到某个城市时面对多条道路,那么城市1的居民就不知道该往哪个方向走了。

  为了解决上述问题,GBQC国领导人决定在一些道路的入口处设置“禁止通行”的路障,以确保城市1的居民从城市1出发,途径每个城市时,都有且仅有一条路可供选择,这样城市1的居民就能顺利搬迁到城市N了。

  现在GBQC国领导人想知道最少需要设置几个路障呢?

Input

  输入包含多组测试数据。

  对于每组测试数据,第一行包含两个整数N (2 ≤ N ≤ 104), M(0 ≤ M ≤ 105),其中N、M的含义同上。接下来一共有M行,每行有三个整数x(1 ≤ x ≤ N)、y(1 ≤ y ≤ N),表示GBQC国有一条由城市x进入通向城市y的单向道路。

Output

  对于每组测试数据,用一行输出一个整数表示最少需要设置几个路障。如果没办法从城市1出发走到城市N,则输出“-1”(不包括引号)。

Sample Input

3 4
1 1
1 2
1 3
1 3

3 2
1 3
3 2

2 0

Sample Output

3
0
-1

Hint

  由于数据量较大,推荐使用scanf和printf。

  算法:广度优先搜索(BFS)。如果有n个岔路,必然要设置n-1个路障。累计路障的总个数。用打擂的方法,比出每个城市的路障累计数的最小值。如果终点没有累计,输出-1,否则,输出终点的路障累计数。

  C++语言源代码如下:

#include <cstdio>
#include <cstdlib>
#include <map>
#include <queue>

using namespace std;

typedef int COUNT;

#define MAX_ROADS 100000

void read_roads( multimap <int,int> & road, const int roads )
{
    int start, end;
    for ( COUNT i = 0 ; i < roads ; i ++ )
    {
        // 读取路线的起点和终点
        scanf( "%d%d", &start, &end );

        // 把整条路作成pair,插入到road这个multimap中
        road.insert(make_pair<int,int>(start, end));
    }
}

void find_ways ( const int cities, const multimap <int,int> & road )
{
    int current_city = 1; // 当前城市编号
    int current_blocks_count = 0; // 到达当前城市累计路障个数
    int next_city, next_blocks_count; // 下个城市编号及累计路障

    queue <int> city;  // queue是STL队列
    map <int, int> city_and_blocks_count; // 建立城市和路障累计数映射

    // 1号城市入队
    city.push( current_city ); // STL队列入队

    // 到达1号城市经历路障总和为零
    city_and_blocks_count.insert( make_pair<int, int>(current_city, current_blocks_count) );

    // 广度优先搜索(BFS)
    while ( !city.empty() ) // 当队列不为空时
    {
        current_city = city.front(); // STL队列,取得队头元素
        city.pop();  // STL队列的出队

        // 读取当前城市可以通往的城市的个数
        current_blocks_count = city_and_blocks_count[current_city];

        // 下一个城市路障累计数目,等于当前城市路障累计数目,加上
        // 通往下一个城市的个数减去一(减去的一就是要走的那条路)
        next_blocks_count = current_blocks_count + road.count(current_city) - 1;

        // 遍历所有可能的“下一个城市”
        for ( multimap <int,int>::const_iterator it = road.lower_bound(current_city) ;
                it != road.upper_bound(current_city); it ++ )
        {
            next_city = (it->second);
            // 如果这个城市已经遍历过
            if ( city_and_blocks_count.count(next_city) > 0 )
            {
                // 判断走当前的路会不会比以往的路路障更少,若更少则刷新记录
                if ( city_and_blocks_count[next_city] > next_blocks_count )
                {
                    // 下个城市入队
                    city.push( next_city );
                    // 刷新该城市的路障累计数
                    city_and_blocks_count[next_city] = next_blocks_count;
                }
            }
            else  // 这个城市未遍历过
            {
                city.push( next_city );  // 城市入队

                // 插入该城市和路障累计数的映射
                city_and_blocks_count.insert( make_pair<int, int>( next_city, next_blocks_count ) );
            }
        }
    }

    // 判断是否遍历过终点
    if ( city_and_blocks_count.count(cities) > 0 )
    {
        // 若遍历过终点,则输出路障累计数
        printf( "%d\n", city_and_blocks_count[cities] );
    }
    else
    {
        // 未到过终点,输出-1
        printf( "-1\n" );
    }
}

void test_case( const int cities, const int roads )
{
    multimap <int,int> road;

    // 自己编写的读取道路数据
    read_roads( road, roads );

    // 自己编写的找路线函数
    find_ways( cities, road );
}

int main (void)
{
    int cities, roads;
    while ( scanf( "%d%d", &cities, &roads ) != EOF )
    {
        test_case( cities, roads );
    }
    return EXIT_SUCCESS;
}

 

posted @ 2012-08-12 17:30  叶剑飞Victor  阅读(381)  评论(0编辑  收藏  举报