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;
}