中南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; }