拓扑排序(差分约束) - Reward - HDU 2647

拓扑排序(差分约束) - Reward - HDU 2647

题意:

格林剧团发工资的时候又到辣,格林剧团来到了圣巢,他们入乡随俗,将工资用圣巢的货币–吉欧来结算。格林团长决定每个人的最低工资是888吉欧,同时有些工人希望自己的工资要比其他某个人的高。格林团长希望在尽可能的少发工资的同时满足其他所有人的需求。请你帮帮格林团长吧,格林团长会邀请你与其在剧团内共舞一场猩红仪式。

Input

题目包含多组输入,第一行输入两个整数n,m,分别表示工人的数量与需求的个数。 接下来m行,每一行都包含两个整数a和b,表示a的工资必须比b的工资高

Output

对于每一组输入,输出格林团长最少花费的吉欧数量。如果格林团长不能满足所有人的需求,输出-1.

Sample Input

2 1
1 2
2 2
1 2
2 1

Sample Output

1777
-1

分析:

差 分 约 束 问 题 。 差分约束问题。

若 A 要 求 比 B 高 , 即 要 求 A > B , 即 A ≥ B + 1 , 我 们 对 应 建 边 A − > B , 边 权 为 1 。 若A要求比B高,即要求A>B,即A≥B+1,我们对应建边A->B,边权为1。 ABA>BAB+1A>B1

最 终 要 求 最 小 值 , 跑 最 长 路 。 最终要求最小值,跑最长路。

由 于 边 权 恒 大 于 0 , 故 我 们 可 以 用 拓 扑 排 序 求 最 短 路 , 时 间 复 杂 度 为 O ( n + m ) 。 由于边权恒大于0,故我们可以用拓扑排序求最短路,时间复杂度为O(n+m)。 0O(n+m)

若 存 在 正 环 或 孤 立 点 , 拓 扑 排 序 得 到 的 元 素 数 量 将 会 小 于 n 。 若存在正环或孤立点,拓扑排序得到的元素数量将会小于n。 n 以 此 来 判 断 是 否 有 解 。 以此来判断是否有解。

若 有 解 , 由 于 要 求 所 有 人 薪 水 不 低 于 888 , 即 对 任 意 的 i , d i s [ i ] ≥ 0 + 888 , 若有解,由于要求所有人薪水不低于888,即对任意的i,dis[i]≥0+888, 888idis[i]0+888

我 们 可 以 建 立 虚 拟 源 点 0 号 点 , d i s [ 0 ] = 0 , 且 0 号 点 与 其 他 点 之 间 均 建 立 一 条 边 权 为 888 的 边 。 我们可以建立虚拟源点0号点,dis[0]=0,且0号点与其他点之间均建立一条边权为888的边。 0dis[0]=00888

等 价 于 我 们 直 接 初 始 化 d i s 数 组 为 888 , 这 样 可 以 省 掉 建 立 虚 拟 源 点 的 过 程 。 等价于我们直接初始化dis数组为888,这样可以省掉建立虚拟源点的过程。 dis888

接 着 按 照 阶 段 跑 最 长 路 即 可 。 接着按照阶段跑最长路即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=10010, M=20010;

int n,m;
int e[M],ne[M],h[N],idx;
int dis[N];
int d[N];
int q[N],hh,tt=-1;

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool topsort()
{
    hh=0,tt=-1;
    for(int i=1;i<=n;i++)
        if(!d[i])
            q[++tt]=i;
            
    while(hh<=tt)
    {
        int u=q[hh++];
        
        for(int i=h[u];~i;i=ne[i])
        {
            int j=e[i];
            d[j]--;
            if(!d[j]) q[++tt]=j;
        }
    }
    
    return tt==n-1;
}   

int cal()
{
    for(int i=1;i<=n;i++) dis[i]=888;  //初始值
    
    for(int i=0;i<n;i++)    //遍历拓扑序,跑最长路
    {
        int u=q[i];
        for(int j=h[u];~j;j=ne[j])
        {
            int k=e[j];
            dis[k]=max(dis[k],dis[u]+1);
        }
    }
    
    int res=0;
    for(int i=1;i<=n;i++) res+=dis[i];
    
    return res;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        idx=0;
        memset(h,-1,sizeof h);
        memset(d,0,sizeof d);
        
        int a,b;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            add(b,a);
            d[a]++;
        }
        
        if(!topsort()) puts("-1");
        else printf("%d\n",cal());
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值