题目介绍
链接: 传送门.
Lee有n个食物,第i个食物有wi份;他还有n个朋友,第i个朋友有两种不同的喜爱食物xi和yi。每个朋友进入厨房时,若两种喜爱的食物都有,则两种都吃掉(try to eat one plate of each of his favorite food types);否则若有一种则吃这剩下的一种;否则他会吃掉Lee。
问以怎样的顺序安排朋友进入,能够使Lee不被吃掉。
题解
考察算法:贪心;构造;(拓扑排序)
注意题意:若有两盘喜爱的食物则都吃;否则若有任一种剩余则只吃该种。
对每个食物有容量w[i],设需求量need[i]为喜欢吃它的人数,
若对所有食物i,w[i]<need[i]则无解。
(1) 对w[i]>=need[i]的食物放入队列中。
(2) 若w[i]>=need[i],则喜欢吃第i种食物的人都可以放到最后,
且这样的人对另一种食物无影响,则另一种食物的需求量可以减少,
(3) 当某一种食物的w[i]初次大于等于need[i],可将其加入操作队列;
重复以上操作,若最终排列的人数等于m,则成立,倒序输出答案即可;否则不成
代码
#include<cstdio>
#include<queue>
#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int base=1e9+7;
int w[maxn],need[maxn],x[maxn],y[maxn],ans[maxn];
bool in_ans[maxn];
vector<int> vec[maxn];
queue<int> que;
int main(){
memset(in_ans,false,sizeof(in_ans));
int n,m,cnt=0; scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
w[x[i]]--; w[y[i]]--;
vec[x[i]].push_back(i);
vec[y[i]].push_back(i);
}
for(int i=1;i<=n;i++){
if(w[i]>=0) que.push(i);
}
int k,size,u,another;
// 每种食物进队一次,friend遍历2遍O(n+m)
while(!que.empty()){
k=que.front(); que.pop();
size=vec[k].size();
for(int i=0;i<size;i++){
// friend编号;排到末尾
u=vec[k][i];
if(!in_ans[u]) ans[++cnt]=u,in_ans[u]=true;
// 另一种喜爱的食物
if(x[u]!=k) another=x[u];
else another=y[u];
w[another]++;
if(w[another]==0) que.push(another);
}
}
if(cnt<m) printf("DEAD\n");
else{
printf("ALIVE\n");
for(int i=cnt;i>=1;i--) printf("%d ",ans[i]);
}
}