注意:这道会重复输入同一个比赛,导致degree变大,要拦阻。
import java.util.Scanner;
/*
* 思想:
* 把数据按要求连起来,就是1->2->3
* ----------------------- 4->3
* 这个时候就可以考虑把这道题看成图的遍历
* 于是想到用拓扑排序来输出拓扑序列,这样就瞒足题目了。
* 拓扑排序:
* 若在有向图G中存在从顶点vi到vj的一条路径,
* 则在顶点序列中顶点vi必须排在顶点vj之前。
* 每次搜索都要把当前结点的邻接的入度减一。
*/
public class Main {
/*
* 静态的成员变量相当于全局变量,这是语法问题。
* n,表示n个比赛队
* m,表示m场比赛
* arc-弧,用来保存每一个顶点的出边和入边,数据结构中是叫邻接矩阵。
* visited表示该顶点是否被访问,用0表示未访问,1表示已被访问
* degree表示该顶点的入度数目
*/
static Scanner sc = new Scanner(System.in);
static int n, m;
static int arc[][] = new int[501][501];
static int visited[] = new int[501];
static int degree[] = new int[501];
public static void main(String[] args) {
while (sc.hasNext()) {
n = sc.nextInt();
m = sc.nextInt();
initialize();
topoloySort();
}
}
/*
* topoloySort-拓扑排序
*/
private static void topoloySort() {
int s = 0;
while (s < n) {
/*
* visited,0表示没有被访问,1表示已被访问
* degree,表示当前结点的入度数目
* 1)先找到入度为0的结点,入队列
* 2)如果i==n,表明该有向图是循环图
* 3)输出结果
* 4)出队列,找到邻接结点
*/
int i = 0;
for (; i < n; i++) {
if (visited[i] == 0 && degree[i] == 0) {
visited[i] = 1;
break;
}
}
if (i == n) {
System.out.println("又向循环图");
return;
}
s++;// 这个要写啊!不然死循环
System.out.print(i + 1);
if (s < n) {
System.out.print(" ");
}
for (int j = 0; j < n; j++) {
if (arc[i][j] == 1) {
degree[j]--;
}
}
}
System.out.println();
}
/*
* initialize-初始化
*/
private static void initialize() {
// 初始化数据
for (int i = 0; i < n; i++) {
visited[i] = 0;
degree[i] = 0;
for (int j = 0; j < n; j++) {
arc[i][j] = 0;
}
}
// 输入数据,把剩下的数据初始化
for (int i = 0; i < m; i++) {
int a = sc.nextInt() - 1;
int b = sc.nextInt() - 1;
if (arc[a][b] == 0) {// 防止重复比赛
arc[a][b] = 1;
degree[b]++;
}
}
}
}
c++代码
/*
一看题目就知道是拓扑排序题,
用c++过。
这道题m的大小是不确定的,
可能会有重复输入,也就是队伍重新比赛。
*/
#include<iostream>
using namespace std;
/*
这题要用邻接矩阵莱来记录比赛情况。
i->j表示i赢j。
arc表示临街矩阵
degree表示入度
visited表示该节点是否被访问
*/
int n,m;
int arc[501][501];
int degree[501];
int visited[501];
void topologySort(int count){
if(count==0){
return;
}
/*
第一步)找到入度为0的结点,
该节点必须是未被访问的,防止重复访问。
然后标记为1,表示该节点已近被访问。
第二步)输出该节点表示的值。
第三步)把该节点的邻居结点的入度减1。
第四步)循环调用指定次数,主要是把所有答案输出。
*/
int i=1;
for(;i<=n;i++){
if(degree[i]==0&&visited[i]==0){
visited[i]=1;
break;
}
}
if(i==n+1){
cout<<"又向循环图"<<endl;
return;
}
cout<<i;
if(count>1){
cout<<" ";
}else{
cout<<endl;
}
for(int j=1;j<=n;j++){
if(arc[i][j]==1){
degree[j]--;
}
}
topologySort(--count);
}
int main(){
while(cin>>n>>m){
/*
初始化数据
*/
for(int i=1;i<=n;i++){
degree[i]=0;
visited[i]=0;
for(int j=1;j<=n;j++){
arc[i][j]=0;
}
}
int a,b;
for(int i=1;i<=m;i++){
cin>>a>>b;
if(arc[a][b]==0){
/*
arc[a][b]==0表示同一个比赛,
不能把入度的值加1。
b结点的入度加1
arc[a][b]表示a->b,a赢b。
*/
degree[b]++;
arc[a][b]=1;
}
}
topologySort(n);
}
return 0;
}
确定比赛名次
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 16652 Accepted Submission(s): 6591
Problem Description
有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。
Input
输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。
Output
给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。
Sample Input
4 3 1 2 2 3 4 3
Sample Output
1 2 4 3
Author
SmallBeer(CML)
Source