HDU 2647 Reward(拓扑排序)

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


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值