I - 排名表
Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
Submit Status
暑假前集训已经过了一半了,我们将会把当前排名公布出来。但是此刻秋实大哥却心急火燎,因为他不慎把排名删除了。
一共有n个人参加排名,每个人都有一个名次,没有哪两个人的名次是相同的。现在秋实大哥掌握的一些情报,比如Ai的名次要先于Bi。(编号从1开始)
你能帮秋实大哥恢复出排名表吗?
Input
第一行一个数字 T (T≤10),表示测试数据组数
每组测试数据,第一行两个数 n(1≤n≤200)和 m(0≤m≤40000),接下来m行,每行两个数a和b(1≤a,b≤N),表示a的名次要先于b
Output
对于每组测试数据,输出一行,从1号到n号每个人的名次。
如果有多个解,让编号为1的人的名次尽量小,然后让编号为2的人的名次尽量小,然后让编号为3的人的名次尽量小......
如果没有解,输出−1
Sample input and output
Sample Input | Sample Output |
---|---|
5 4 0 4 1 1 1 4 2 1 2 2 1 4 1 2 1 4 1 3 2 |
1 2 3 4 -1 -1 2 1 3 4 1 3 2 4 |
Hint
注意可能会有重边
解题思路:
首先,本题正向拓扑排序是不行的,即如果这样建边:
U 的名次先于 V , 即 V 向 U连边
因为题意比较绕,下文都这样叙述:
排名号越高,其名次越低,第一名的排名号是 1 ,它的名次是最高的.
我们按照 v 向 u建边,意思是v的排名号高于u.
无法保证
让编号为1的人的名次尽量小,然后让编号为2的人的名次尽量小,然后让编号为3的人的名次尽量小.
è 让编号为 1 的人的排名号尽量大,然后让编号为 2 的人的排名号尽量大….
我们拓扑排序的顺序是这样的:
每次寻找入度为 0 的点,将排名号赋给这个点,删边,重复.
如果有多个点入度都是 0 呢?我们不假思索的这样想:
我们为了保证题目条件,会使得我们会尽量保证这个点的编号尽量小(这样我们看起来符合了题目条件:编号小的人排名号尽量高),但是我们忽略了,题目要求是先尽力保证编号1的人,之后才是编号2的人….
所以,正向建边是错误的
注意,上面的证明非常不严格(Even it‘s a mistake),如果小伙伴谁有更好的证明,可在下面回复
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #define pb push_back using namespace std; const int maxn = 200 + 10; int n,m,c[maxn],indig[maxn]; vector<int>E[maxn]; bool existedge[maxn][maxn]; int out[maxn]; bool dfs(int cur) { if (c[cur] == 1) return true; c[cur] = -1; for(int i = 0 ; i < E[cur].size() ; ++ i) { int nextnode = E[cur][i]; if (c[nextnode] == -1) return false; if (!c[nextnode] && !dfs(nextnode)) //exist 环 return false; } c[cur] = 1; return true; } typedef struct point { int id,indig; friend bool operator < (const point & x,const point & y) { return x.indig < y.indig; } point(int id,int indig) { this->id = id , this->indig = indig; } }; vector<point>v; bool used[maxn]; void ansset() { memset(used,false,sizeof(used)); for(int i = 1 ; i <= n ; ++ i) v.pb(point(i,indig[i])); sort(v.begin(),v.end()); int rank = 0; for(int i = 1 ; i <= n ; ++ i) { int choose = v[0].id; int pos = 0; while( pos < v.size() && !v[pos].indig) choose = max(choose,v[pos++].id); out[choose] = n - rank++; used[choose] = true; for(int i = 0 ; i < E[choose].size() ; ++ i) indig[E[choose][i]]--; v.clear(); for(int i = 1 ; i <= n ; ++ i) if(!used[i]) v.pb(point(i,indig[i])); sort(v.begin(),v.end()); } } int main(int argc,char *argv[]) { int Case; scanf("%d",&Case); while(Case--) { scanf("%d%d",&n,&m); memset(c,0,sizeof(c)); memset(existedge,false,sizeof(existedge)); memset(indig,0,sizeof(indig)); while(m--) { int u,v; scanf("%d%d",&u,&v); if (existedge[v][u]) continue; E[v].pb(u); existedge[v][u] = true; indig[u] ++; } int ans = 1; for(int i = 1 ; i <= n ; ++ i) if (!dfs(i)) { ans = 0; break; } if (!ans) printf("-1\n"); else { v.clear(); ansset(); for(int i = 1 ; i <= n ; ++ i) i == 1 ? printf("%d",out[i]) : printf(" %d",out[i]); printf("\n"); } for(int i = 1 ; i <= n ; ++ i) E[i].clear(); } return 0; }