HDU 1285 确定比赛名次 确定比赛名次

传送门

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 17915    Accepted Submission(s): 7169


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
 
Recommend
lcy
-------------------------------------------------------------------------------------------------------------------------
我的错误解法
 
#include <bits/stdc++.h>
using namespace std;
const int N(500+5);
vector<int> g[N];
bool vis[N];
int topo[N], tot;
void dfs(int u){
    vis[u]=1;
    for(int i=0; i<g[u].size(); i++){
        int &v=g[u][i];
        if(vis[v]) continue;
        dfs(v);
    }
    topo[--tot]=u;
}
int main(){
    for(int n, m; ~scanf("%d%d", &n, &m);){
        for(int i=1; i<=n; i++) g[i].clear();
        for(int u, v; m--;) scanf("%d%d", &u, &v), g[u].push_back(v);
        for(int i=1; i<=n; i++) sort(g[i].begin(), g[i].end(), greater<int>());
        memset(vis, 0, sizeof(vis)); tot=n;
        for(int i=n; i; i--) if(!vis[i]) dfs(i);
        printf("%d", topo[0]); for(int i=1; i<n; i++) printf(" %d", topo[i]); puts(""); 
    }
}
首先总的想法是DFS拓扑排序,由于题目要求输出字典序最小的排列,对DFS的顺序做一些修改:
(1)将每个节点的邻接表从大到小排序
(2)在主函数内,按照节点编号从大到小的顺序调用DFS
这两个修改虽然方向是对的,但还是有问题的。
比如,样例
5 4
5 2
2 4
5 3 
3 1
正确输出是
5 2 3 1 4
但我的代码输出:
5 2 4 3 1
这个题目的输出要解决的问题是:
保证先后关系不确定的节点中,序号小的在前。
(注意,上面用的词是先后关系,而非胜负关系。因为,假如知道了 A胜B, B胜C 那么A, B, C三者的先后一定是 A B C 但A和C的胜负关系是不知道的。)
能否在不改变DFS框架的前提下,使输出正确呢?
 ---------------------------------------------------------------------------------------
正解是:迭代
每次选择入度为0且编号最小的点u输出,将u删除,并将u的临接表中的点的入度减一。
(这种解法不需要处理重边
 
#include <bits/stdc++.h>
using namespace std;
const int N(500+5);
vector<int> g[N];
int in[N];
int main(){
    for(int n, m; ~scanf("%d%d", &n, &m);){
        for(int i=1; i<=n; i++) g[i].clear();
        memset(in, 0, sizeof(in));
        for(int u, v; m--;) scanf("%d%d", &u, &v), g[u].push_back(v), in[v]++;
        for(int i=0; i<n; )
            for(int u=1; u<=n; u++){
                if(in[u]==0){
                    if(!i++) printf("%d", u);
                    else printf(" %d", u);
                    in[u]=-1;
                    for(int j=0; j<g[u].size(); j++){
                        int &v=g[u][j]; in[v]--;
                    }
                    break;
                }
            }
        puts("");
    }
}
还可用堆优化
#include <bits/stdc++.h>
using namespace std;
const int N(500+5);
vector<int> g[N];
int in[N];
typedef pair<int,int> P;
priority_queue<P, vector<P>, greater<P> > que;
int main(){
    for(int n, m; ~scanf("%d%d", &n, &m);){
        for(int i=1; i<=n; i++) g[i].clear();
        memset(in, 0, sizeof(in));
        for(int u, v; m--;) scanf("%d%d", &u, &v), g[u].push_back(v), in[v]++;
        for(int i=1; i<=n; i++) que.push({in[i], i});
        bool first=true;
        while(!que.empty()){
            P top=que.top(); que.pop(); int &u=top.second;
            if(in[u]!=top.first) continue;
            if(first) printf("%d", u), first=false;
            else printf(" %d", u);
            for(int i=0; i<g[u].size(); i++){
                int &v=g[u][i];
                que.push({--in[v], v});
            }
        }
        puts("");
    }
}