[日常训练] 保存名画

【问题描述】

在一个Byteland上有一副非常有名的图画需要被封存。这个作品需要在2个实验室进行处理。这个处理过程被分为许多步骤。对于每个步骤,我们知道它必须要在哪个实验室进行。
在两个实验室之间运输这些这些美丽但又易碎的画会带来额外的风险,因此这个运输的过程需要尽可能的被避免。理想情况下所有的工作都会在第一个实验室做完,然后剩下的在另一个实验室完成,但是有些步骤必须在另一步骤完成之后才能完成。你的任务是找到一个工作的顺序,让这个被运输的过程最少。输出需要运输的次数。

【输入格式】

第一行N和M,代表着N个步骤,和M个先后关系。下面一行N个数字,第I个数字是1或2,代表了第I个工作需要在哪个实验室完成,下面M行I,J,代表了第I个工作必须在第J个工作前完成。
你可以认为这个题目必有解

【输出格式】

输出最少需要的运输次数。

【输入样例】

5 6
1 2 1 2 1
1 2
1 3
2 4
3 4
2 5
3 5

【输出样例】

2

【数据范围与约定】

30%数据保证n<=5。
100%数据中n<=100000,m<=1000000。

【30分】全排列搜索

【100分】拓扑排序 + 贪心

对于第1或第2个实验室,我们分别建两个队列存储当前进行能够在第1或第2个实验室完成的工作。然后进行拓扑排序:
[1]、每次选取入度为0的点加入两个队列;
[2]、要使运输次数最少,就肯定要尽量在同一个队列里选取能完成的步骤,因此记一个状态 st ,表示当前我们所选取的是第 st 个队列;
[3]、每次从第 st 个队列中取出队头元素作为进行的步骤,那么在该步骤之后完成的步骤入度都要减少1;
[4]、如果一个队列为空,表示现在我们已经不能一直在同一个实验室里完成一些步骤,因此要换一个队列来取出队头元素,同时运输次数加一,直到两个队列都为空为止。
另外因为 st 的最初值可能为 1 2(最初可能在任意一个实验室里),所以对于这两种情况,我们都要计算一遍,最后对两者取一个最小值。

【代码】
#include <iostream>
#include <cstdio>

using namespace std;
const int N = 1e5 + 5, M = 1e6 + 5;
int rin[N], cur[N], a[N], n, m; 
int lst[N], nxt[M], to[M], T;

inline int get()
{
    int res; char ch;
    while ((ch = getchar()) < '0' || ch > '9');
    res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return res;
}

inline int Min(const int &x, const int &y) {return x < y ? x : y;}

inline void addEdge(const int &x, const int &y)
{
    nxt[++T] = lst[x]; lst[x] = T; to[T] = y; rin[y]++;
}

inline int Query(int st)
{
    static int Q[N][2], t[2], w[2]; int x, y, Ans = 0;
    t[0] = t[1] = w[0] = w[1] = 0;
    for (int i = 1; i <= n; ++i) cur[i] = rin[i];
    for (int i = 1; i <= n; ++i)
      if (!cur[i]) Q[++w[a[i]]][a[i]] = i;
    while (t[0] < w[0] || t[1] < w[1])
    {
        if (t[st] == w[st]) st ^= 1, Ans++;
        x = Q[++t[st]][st];
        for (int i = lst[x]; i; i = nxt[i])
         if (!(--cur[y = to[i]])) Q[++w[a[y]]][a[y]] = y;
    }
    return Ans;
}

int main()
{
    freopen("save.in", "r", stdin);
    freopen("save.out", "w", stdout);
    n = get(); m = get(); int x, y;
    for (int i = 1; i <= n; ++i) a[i] = get() - 1;
    while (m--) 
    {
        x = get(); y = get();
        addEdge(x, y);
    }
    printf("%d\n", Min(Query(0), Query(1)));
    fclose(stdin); fclose(stdout);
    return 0;
} 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值