Link
思维,贪心,拓扑排序 2400
题意
有 n n n种菜,每种菜都有 w i w_i wi碟,你有 m m m个朋友,每个朋友都有两种喜欢的菜,你按照某个排序让朋友一个一个来吃菜,如果现在桌上有这个朋友喜欢的菜,他就会每种都吃一碟,但是如果两种菜都没了,你就会死。问你最后你会不会死,如果不会输出这个排序。
思路
显然,如果第
i
i
i 种菜的需求不大于
w
i
w_i
wi,那么吃这种菜的人一定能吃到,我们贪心地把这些人往后排。同时对于这些人,它们已经吃了一种菜
i
i
i了,那么对于他们喜欢的另一种菜
j
j
j,需求就会少1。发现这一过程类似于拓扑排序,考虑建一张图:
n
n
n个点分别为每个菜,而
2
∗
m
2*m
2∗m条边
<
u
,
v
>
<u,v>
<u,v>分别连接每个朋友喜欢的两种菜
u
,
v
u, v
u,v,并分别用
w
[
i
]
,
i
n
[
i
]
w[i],in[i]
w[i],in[i]表示第
i
i
i个菜有多少碟以及需求量,容易发现需求量和拓扑排序里的入度很像。起初把需求量不超过碟数的点加入队列,然后每次将队首元素与其到达的点
v
v
v 入度减1,若
i
n
[
v
]
=
=
w
[
v
]
in[v] == w[v]
in[v]==w[v]则加入队列。同时将这条边所代表的朋友加入栈(无论是否in[v] == w[v]),最后判断栈中元素数量是否为m即可。
代码
int n, m;
int w[maxn];
vector<pair<int, int> > e[maxn];
int in[maxn];
bool vis[maxn];
queue<int> q;
stack<int> ans;
void solve() {
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> w[i];
for(int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
e[x].pb(make_pair(y,i));
e[y].pb(make_pair(x,i));
in[x]++; in[y]++;
}
for(int i = 1; i <= n; i++) {
if(in[i] <= w[i]) q.push(i);
}
while(!q.empty()) {
int x = q.front();
q.pop();
for(auto i : e[x]) {
int to = i.first;
int a = i.second;
in[to]--;
if(!vis[a])
{
ans.push(a);
vis[a] = 1;
}
if(in[to] == w[to])
q.push(to);
}
}
if(ans.size() < m) {
cout << "DEAD\n";
return;
}
cout << "ALIVE\n";
while(!ans.empty()) {
cout << ans.top() << ' ';
ans.pop();
}
cout << endl;
}