小C与桌游
解析
首先,这道题显然和拓扑排序有关
其次,读题后发现,题目要求求出 “最优情况” 和 “最劣情况” 两个答案
我们不妨在求解时将这两个问题分开。
对于“最优情况”,我们显然可以贪心的取编号最小的入度为0的点扩展。每次与之前走过的最大的点比较,然后更新即可
然后对于第二种情况,最劣情况,我们能不能效仿第一种的贪心呢,每次从编号最大且入度为0的点的开始拓展。
答案显然是否定的,我们可以很容易地找到下面一组反例
我们如果按照上面的贪心方法,我们可以得到ans=3
2—3---1—4
然而真正的答案应该是ans=2
2—1---4—3
那么我们考虑,如果我们走到一个小于当前最大值的点,那么是不影响答案的,何乐而不为呢?
于是我们想到,再建一个小根堆,当小根堆堆顶小于最大值时,就拓展该点,不然在去找大根堆顶
代码
#include<bits/stdc++.h>
using namespace std;
int bj[1000005],vis[1000005],bjj[1000005];
int first[1000005],tot,ans1,ans2,nxt[1000005],to[1000005],n,m,bef,cnt;
priority_queue<int>q;
priority_queue<int>p;
priority_queue<int>w;
void add(int x,int y)
{
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
int main()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
bj[y]++;
}
for(int i=1;i<=n;i++)
{
vis[i]=bj[i];
if(!bj[i])
{
q.push(i);
p.push(-i);
w.push(-i);
}
}
while(!p.empty())
{
int v=-p.top();
p.pop();
if(v>bef)
{
bef=v;
ans2++;
}
for(int i=first[v];i;i=nxt[i])
{
int u=to[i];
vis[u]-=1;
if(!vis[u]) p.push(-u);
}
}
bef=0;
while(!q.empty())
{
int k=-w.top();
while(bjj[k]&&!w.empty())
{
w.pop();
k=-w.top();
}
if(k>bef||w.empty())
{
k=q.top();
q.pop();
while(!q.empty()&&bjj[k])
{
k=q.top();
q.pop();
}
}
if(k==-w.top())
w.pop();
bjj[k]=1;
if(k>bef)
{
bef=k;
ans1++;
}
for(int i=first[k];i;i=nxt[i])
{
int u=to[i];
bj[u]-=1;
if(!bj[u])
{
w.push(-u);
q.push(u);
}
}
}
printf("%d\n%d",ans2,ans1);
return 0;
}