HDU 2647 Reward(拓扑排序)
http://acm.hdu.edu.cn/showproblem.php?pid=2647
题意:
老板要给N个员工发工资,要求每个人最少888块且要满足M个要求.比如1号员工的工资要比2号员工的工资多.老板准备满足所有要求且最终发的奖金总数要最少.如果可行输出总数,不可行输出-1.
分析:
其实就是将所有员工的奖金拓扑排序,然后从888开始安排.不过要注意对于任意奖金数(比如889或888)可能有多个员工获得同样的奖金.
实现方法很多,这里我采用网上一位大牛的方法.将整个拓扑图分层,位于第0层的是那些可以获得888奖金的,位于第1层的是可以获得889奖金的,依次类推.最终的奖金总数就是888*N+(所有节点层数总和).
依然用的拓扑排序的方法,不过我们分解图的过程中只需要计算出每个节点所在的树层次即可.求一个节点的深度一定要注意:由于我们采用了队列结构,所以若u是正好能使得v入度归0的点,那么u一定是所有指向v点的点中深度最大的(这个自己画个图验证一下).
AC代码:
#include<cstdio> #include<cstring> #include<queue> #include<vector> using namespace std; const int maxn=10000+10; const int maxm=20000+10; int n,m; int ans; //ans记录所有节点层数dep的总和 struct Node { int in,dep; //入度与层号 int head; //指向第一条边号,head为0表示无边 }nodes[maxn]; struct Edge { int to; //尾节点 int next; //下一条边 }edges[maxm]; //边从1开始计数 void add_edge(int i,int u,int v)//添加一条从u->v的边 { edges[i]=(Edge){v,nodes[u].head}; nodes[u].head=i; nodes[v].in++; } bool topo() { queue<int> Q; ans=0; int sum=0; for(int i=1;i<=n;i++)if(nodes[i].in==0) Q.push(i); while(!Q.empty()) { int u=Q.front(); Q.pop(); sum++; ans+= nodes[u].dep; for(int e=nodes[u].head;e;e=edges[e].next) { int v=edges[e].to; if(--nodes[v].in==0)//这里要注意:若u是正好能使得v入度归0的点,那么u一定是所有指向v点的点中深度最大的 { Q.push(v); nodes[v].dep = nodes[u].dep+1; } } } return sum==n; } int main() { while(scanf("%d%d",&n,&m)==2) { memset(nodes,0,sizeof(nodes)); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(i,v,u); } printf("%d\n",topo()?888*n+ans:-1); } return 0; }