【问题描述】
在一个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
或
【代码】
#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;
}