题目描述
图一表示一次街道赛跑的跑道。可以看出有一些路口(用 0 0 0 到 N N N 的整数标号),和连接这些路口的箭头。路口 0 0 0 是跑道的起点,路口 N N N 是跑道的终点。箭头表示单行道。运动员们可以顺着街道从一个路口移动到另一个路口(只能按照箭头所指的方向)。当运动员处于路口位置时,他可以选择任意一条由这个路口引出的街道。

图一:有 10 个路口的街道
 一个良好的跑道具有如下几个特点:
 1.每一个路口都可以由起点到达。
 2.从任意一个路口都可以到达终点。
 3.终点不通往任何路口。
运动员不必经过所有的路口来完成比赛。有些路口却是选择任意一条路线都必须到达的(称为“不可避免”的)。在上面的例子中,这些路口是 0 , 3 , 6 , 9 0,3,6,9 0,3,6,9 。对于给出的良好的跑道,你的程序要确定“不可避免”的路口的集合,不包括起点和终点。
假设比赛要分两天进行。为了达到这个目的,原来的跑道必须分为两个跑道,每天使用一个跑道。第一天,起点为路口  
     
      
       
       
         0 
        
       
      
        0 
       
      
    0 ,终点为一个“中间路口”;第二天,起点是那个中间路口,而终点为路口  
     
      
       
       
         N 
        
       
      
        N 
       
      
    N 。对于给出的良好的跑道,你的程序要确定“中间路口”的集合。如果良好的跑道  
     
      
       
       
         C 
        
       
      
        C 
       
      
    C 可以被路口  
     
      
       
       
         S 
        
       
      
        S 
       
      
    S 分成两部分,这两部分都是良好的,并且  
     
      
       
       
         S 
        
       
      
        S 
       
      
    S 不同于起点也不同于终点,同时被分割的两个部分满足下列条件:
 (1)它们之间没有共同的街道
 (2) 
     
      
       
       
         S 
        
       
      
        S 
       
      
    S 为它们唯一的公共点,并且  
     
      
       
       
         S 
        
       
      
        S 
       
      
    S 作为其中一个的终点和另外一个的起点。那么我们称  
     
      
       
       
         S 
        
       
      
        S 
       
      
    S 为“中间路口 ”。在例子中只有路口  
     
      
       
       
         3 
        
       
      
        3 
       
      
    3 是中间路口。
输入格式
输入文件包括一个良好的跑道,最多有  
     
      
       
       
         50 
        
       
      
        50 
       
      
    50 个路口, 
     
      
       
       
         100 
        
       
      
        100 
       
      
    100 条单行道。
 一共有  
     
      
       
       
         N 
        
       
         + 
        
       
         2 
        
       
      
        N+2 
       
      
    N+2 行,前面  
     
      
       
       
         N 
        
       
         + 
        
       
         1 
        
       
      
        N+1 
       
      
    N+1 行中第  
     
      
       
       
         i 
        
       
      
        i 
       
      
    i 行表示以  
     
      
       
       
         i 
        
       
         − 
        
       
         1 
        
       
      
        i -1 
       
      
    i−1 为起点的街道,每个数字表示一个终点。行末用  
     
      
       
       
         − 
        
       
         2 
        
       
      
        -2 
       
      
    −2 作为结束。
 最后一行只有一个数字  
     
      
       
       
         − 
        
       
         1 
        
       
      
        -1 
       
      
    −1。
输出格式
你的程序要有两行输出:
 第一行包括:跑道中“不可避免的”路口的数量,接着是这些路口的序号,序号按照升序排列。
 第二行包括:跑道中“中间路口”的数量,接着是这些路口的序号,序号按照升序排列。
样例
样例输入1
1 2 -2
3 -2
3 -2
5 4 -2
6 4 -2
6 -2
7 8 -2
9 -2
5 9 -2
-2
-1
样例输出1
2 3 6
1 3
题解
1
关于第  
     
      
       
       
         1 
        
       
      
        1 
       
      
    1 问,求不可避免的点,即去掉该点后不能从起点到终点。
 故可以依次枚举每个点,去掉该点后从  
     
      
       
       
         0 
        
       
      
        0 
       
      
    0 进行 dfs,如果不能到终点  
     
      
       
       
         N 
        
       
      
        N 
       
      
    N,该点就是不可避免的点。
void dfs(int x){
	if(x == n){//能走到 n
		fl = 1;
		return;
	}
	if(fl){
		return;
	}
	//遍历 i 连接的边
	for(auto i : v[x]){
		if(!f[i]){
			f[i] = 1;
			dfs(i);
			f[i] = 0;
		}
	} 
}
for(int i = 1; i < n; ++ i){
	//fl 标记能不能到终点
	fl = 0;
	//标记 dfs 中走没走到某点的数组
	memset(f, 0, sizeof(f));
	//将 i 标记为 1,相当于把 i 删掉
	f[i] = 1;
	f[0] = 1;
	dfs(0);
	//不可避免的点
	if(fl == 0){
		q[++ l] = i;
	}
}
2
关于第 2 2 2 问,求中间路口。
如果第 i i i 个点是中间路口,就必须保证第 i i i 个点是不可避免的点。如果第 i i i 个点不是不可避免的点,那么一定存在至少一条路径能直接从 0 0 0 到 n n n。
问题变为了如何求两部分间是否有共同的街道。
先把第  
     
      
       
       
         i 
        
       
      
        i 
       
      
    i 个点堵上,从  
     
      
       
       
         0 
        
       
      
        0 
       
      
    0 开始 dfs,将所有能走到的点标记为  
     
      
       
       
         1 
        
       
      
        1 
       
      
    1,即第  
     
      
       
       
         1 
        
       
      
        1 
       
      
    1 天能到的点。
 再从第  
     
      
       
       
         i 
        
       
      
        i 
       
      
    i 个点进行遍历,如果当前走到的点已经标记为  
     
      
       
       
         1 
        
       
      
        1 
       
      
    1 了,则该点不为中间路口。
void dfs1(int x){
	//标记遍历的点
	f[x] = 1;
	f1[x] = 1;
	for(auto i : v[x]){
		if(!f[i]){
			dfs1(i);
		}
	} 
	f[x] = 0;
}
int fl2 = 1;
void dfs2(int x){
	//碰到标记的点
	if(f1[x] == 1){
		fl2 = 0;
		return;
	}
	if(!fl2){
		return;
	}
	//标记为 2
	f1[x] = 2;
	f[x] = 1;
	for(auto i : v[x]){
		if(!f[i]){
			dfs2(i);
		}
	} 
	f[x] = 0;
}
for(int i = 1; i <= l; ++ i){
	//清空
	fl2 = 1;
	memset(f, 0, sizeof(f));
	memset(f1, 0, sizeof(f1));
	f1[q[i]] = 2;
	f[q[i]] = 1; 
	f[0] = 1;
	dfs1(0);
	memset(f, 0, sizeof(f));
	dfs2(q[i]);
	//中间路口
	if(fl2){
		q2.push(q[i]);
	}
}
综上,我们就可以得到代码了。
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> v[60];//存图
dfs;
dfs1;
dfs2;
int main(){
	for(n = 0; ; ++ n){
		bool p = 0;
		int g;
		while(scanf("%d", &g) != EOF){
			if(g == -2){
				break;
			}
			if(g == -1){
				p = 1;
				break;
			}
			v[n].push_back(g);
		}
		if(p){
			break; 
		} 
	}
	-- n;
	# 1
	输出 q
	# 2
	输出 q2
	return 0;
}
禁止抄袭!!!


















