PKU 3687 Labeling Balls(拓扑排序)
题目大意:原题链接
给出N个未编号的质量各不相同的球,以及它们质量轻重的大小关系,给它们从1~N贴标签编号,无重复。问是否存在可行的编号方法,不存在输出-1,
如果存在则输出唯一一种方案,此方案是使得编号小的球的重量尽量轻,先是编号为1的重量要最轻,其次比编号2,以此类推......
思路:当解有多组时,编号小的质量尽量小。所以就采用逆拓扑排序(正向的贪心不能完全保证序号小的节点尽量排在前面。仔细思考~~~~~),
按编号从大到小,找质量最大的。这样,小标签就都留给了质量小的。比如4-->1,3-->2这个图(答案是2 4 3 1),
如果正向的话先出来的是3,然后是2,然后是4,最后才是1,输出(3 2 4 1)是个反例,而反向的话却可以保证把小的尽可能留给小标号的
解法一:逆拓扑排序+贪心
#include<cstdio> #include<cstring> #define maxn 210 using namespace std; int n,m,out[maxn],q[maxn]; int i,j,k,graph[maxn][maxn]; bool Toposort(){ for(i=n;i>=1;i--){//按编号从大到小 for(j=n;j>=1;j--){//找质量最大的 if(!out[j]){ q[j]=i; out[j]=-1; break; } }//j未通过break退出循环,说明无零出度节点,有环 if(j<1) return false; for(k=1;k<=n;k++){ if(graph[k][j]) out[k]--; } } return true; } int main(){ int T,u,v; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); memset(graph,0,sizeof(graph)); memset(out,0,sizeof(out)); for(i=0;i<m;i++){ scanf("%d%d",&u,&v); if(!graph[u][v]){ graph[u][v]=true; out[u]++; }//注意重边 } if(Toposort()){ for(i=1;i<=n;i++){ if(i!=n) printf("%d ",q[i]); else printf("%d\n",q[i]); } } else printf("-1\n"); } }
解法二:逆拓扑排序+优先队列
#include<cstdio> #include<queue> #include<cstring> #define maxn 210 using namespace std; int n,m,out[maxn],va[maxn]; int graph[maxn][maxn]; priority_queue<int> que; bool Toposort() { for(int i=1;i<=n;i++){ if(!out[i]) que.push(i); } if(que.empty()) return false;//注意 int t=n; while(!que.empty()){ int j=que.top(); que.pop(); va[j]=t--; for(int k=1;k<=n;k++){ if(graph[k][j]){ graph[k][j]=0; out[k]--; if(!out[k]) que.push(k); } } } if(t) return false;//注意 return true; } int main() { int T,u,v; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); memset(graph,0,sizeof(graph)); memset(out,0,sizeof(out)); for(int i=0;i<m;i++){ scanf("%d%d",&u,&v); if(!graph[u][v]){ graph[u][v]=true; out[u]++; }//注意重边 } if(Toposort()){ for(int i=1;i<n;i++) printf("%d ",va[i]); printf("%d\n",va[n]); } else printf("-1\n"); } }