2017-2018 ACM-ICPC NEERC B题Berland Army 拓扑排序+非常伤脑筋的要求
题目链接:http://codeforces.com/contest/883/problem/B
There are n military men in the Berland army. Some of them have given orders to other military men by now. Given m pairs (xi, yi), meaning that the military man xi gave the i-th order to another military man yi.
It is time for reform! The Berland Ministry of Defence plans to introduce ranks in the Berland army. Each military man should be assigned a rank — integer number between 1 and k, inclusive. Some of them have been already assigned a rank, but the rest of them should get a rank soon.
Help the ministry to assign ranks to the rest of the army so that:
- for each of m orders it is true that the rank of a person giving the order (military man xi) is strictly greater than the rank of a person receiving the order (military man yi);
- for each rank from 1 to k there is at least one military man with this rank.
The first line contains three integers n, m and k (1 ≤ n ≤ 2·105, 0 ≤ m ≤ 2·105, 1 ≤ k ≤ 2·105) — number of military men in the Berland army, number of orders and number of ranks.
The second line contains n integers r1, r2, ..., rn, where ri > 0 (in this case 1 ≤ ri ≤ k) means that the i-th military man has been already assigned the rank ri; ri = 0 means the i-th military man doesn't have a rank yet.
The following m lines contain orders one per line. Each order is described with a line containing two integers xi, yi (1 ≤ xi, yi ≤ n, xi ≠ yi). This line means that the i-th order was given by the military man xi to the military man yi. For each pair (x, y) of military men there could be several orders from x to y.
Print n integers, where the i-th number is the rank of the i-th military man. If there are many solutions, print any of them.
If there is no solution, print the only number -1.
5 3 3
0 3 0 0 2
2 4
3 4
3 5
1 3 3 2 2
7 6 5
0 4 5 4 1 0 0
6 1
3 6
3 1
7 5
7 1
7 4
2 4 5 4 1 3 5
2 2 2
2 1
1 2
2 1
-1
题意为有n个士兵,现在要给他们赋军衔1-k,给m条边,每条边u->v表示u可以命令v,即u的军衔比v大。输出赋衔方案。
关键的要求:1.输入给定了一些士兵的军衔。只有部分士兵的军衔可供你修改。
2.1-k这k个军衔每个都要有人。即对于任意一个军衔等级i,n个人中必须要有人获得这个军衔。
2真的是非常伤脑筋的一个要求。
最后队友想出了贪心:
先正反跑两次拓扑排序,
得到在考虑不考虑每个军衔情况下的,每个人的最高和最低可能军衔。
然后以最高可能军衔为第一关键字和最低可能军衔为第二关键字排序。
由于父亲的最高可能军衔一定比儿子大,所以父亲一定在前面。
然后贪心式地赋衔就可以了,注意一下如果第i个人的low等于目前贪心的top那么也要赋值为top就可以了。
AC代码
#include<bits/stdc++.h> using namespace std; const int MAXN=210000; int n,m,k; vector<int> head[MAXN]; vector<pair<int, int> > rnk[MAXN]; int solid[MAXN]; int low[MAXN],ans[MAXN]; int high[MAXN]; int in[MAXN]; int flag=1; struct Edge { int x,y; Edge() {} }edge[MAXN]; void Input() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;++i) { scanf("%d",&solid[i]); } int u,v; for(int i=1;i<=m;++i) { scanf("%d%d",&edge[i].x,&edge[i].y); } } void init() { for(int i=1;i<=n;++i) { head[i].clear(); in[i]=0; } } void toposort(int now[],int type) { queue<int > q; int cnt=0; for(int i=1;i<=n;++i) { if(!in[i]) { q.push(i); ++cnt; } } while(!q.empty()) { int u=q.front();q.pop(); for(int i=0;i<head[u].size();++i) { int v=head[u][i]; if(type<0) { now[v]=min(now[v],now[u]-1); } else { now[v]=max(now[v],now[u]+1); } if(--in[v]<=0) { ++cnt; q.push(v); } } } if(cnt!=n) { flag=0; } } void work() { int u,v; flag=1; init(); for(int i=1;i<=n;++i) { if(solid[i]) high[i]=solid[i]; else high[i]=k; } for(int i=1;i<=m;++i) { u=edge[i].x,v=edge[i].y; head[u].push_back(v); in[v]++; } toposort(high,-1); if(flag==0) { printf("-1\n"); return; } init(); for(int i=1;i<=n;++i) { if(solid[i]) low[i]=solid[i]; else low[i]=1; } for(int i=1;i<=m;++i) { u=edge[i].y;v=edge[i].x; head[u].push_back(v); in[v]++; } toposort(low,1); if(flag==0) { printf("-1\n"); return; } for(int i=1;i<=n;++i) { if(low[i]>high[i]) { printf("-1\n");return; } } int top=k; for(int i=1;i<=k;++i) rnk[i].clear(); for(int i=1;i<=n;++i) { rnk[high[i]].push_back(make_pair(-low[i],i)); } set< pair<int,int> > S; set< pair<int,int> >::iterator it; for(int i=0;i<rnk[k].size();++i) { S.insert(rnk[k][i]); } pair<int ,int > tmp; while(top) { if(S.empty()) { printf("-1\n"); return; } it=S.begin(); tmp=*it; ans[tmp.second]=top; S.erase(it); while(1) { it=S.begin(); tmp=*it; if(-tmp.first<top) break; ans[tmp.second]=top; S.erase(it); } --top; for(int i=0;i<rnk[top].size();++i) { S.insert(rnk[top][i]); } } while(!S.empty()) { tmp=*it; ans[tmp.second]=1; S.erase(it); } for(int i=1;i<=n;++i) printf("%d ",ans[i]); printf("\n"); } int main() { // freopen("in.txt","r",stdin); Input(); work(); return 0; }