正解有点巧妙没想出来。学到了,以后这种题,最早呀,最晚呀,拓扑呀,可以往反图方面想想。
参考这位大佬的 https://blog.csdn.net/weixin_34377065/article/details/86020916
应该不少人一开始想的是正着做,弄个优先队列优先让时限早的先飞。这样听起来很对,其实是错的,上面博客有一句话说得很好:因为当前的k小并不意味着后面的点k也小。
这里给出一个正着做的错误:3->5 4->2 这里的数字代表时限,箭头代表连边。合法情况应该是4->2->3->5。但是因为优先队列优先时限小的会变成3->4->2->5显然是错误的。因为当前小不代表后面小,当前大也不代表后面大。
那为什么倒着做是正确的呢?因为题目本身给的时限的意思就是最晚飞行时间,那么我们倒着做就是让后面时限大的飞机尽量晚飞,题目保证有解那么肯定不会出现 时限最大的飞机最晚飞 然后无解这种情况。
这是卡着时限飞的意思,能拖就拖。
那么第二问的最早飞行时间,我们枚举飞机i,意思就是飞机i最早只能在这个时间飞,再早了就会影响别人。那么在反图上就是,强制让飞机i不飞(因为在反图的缘故,拖着不飞就是尽量早飞),直到它影响了别人的时刻,这就是它的最早飞行时刻,因为再早了就影响别人了。
这道题还是非常有意思的,值得多多思考。
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> pii; const int N=1e4+10; int n,m,deg[N],k[N],p[N],rc[N]; vector<int> G[N]; priority_queue<pii> q; void toposort() { int cnt=0; for (int i=1;i<=n;i++) if (deg[i]==0) q.push(make_pair(k[i],i)); while (!q.empty()) { pii x=q.top(); q.pop(); p[++cnt]=x.second; for (int i=0;i<G[x.second].size();i++) { int y=G[x.second][i]; if (--deg[y]==0) q.push(make_pair(k[y],y)); } } for (int i=cnt;i>=1;i--) printf("%d ",p[i]); puts(""); } int solve(int t) { while (!q.empty()) q.pop(); for (int i=1;i<=n;i++) deg[i]=rc[i]; deg[t]=0x3f3f3f3f; //把t点度数设为无穷大,无法入队 for (int i=1;i<=n;i++) if (deg[i]==0) q.push(make_pair(k[i],i)); for (int i=n;i;i--) { if (q.empty()) return i; pii x=q.top(); q.pop(); if (x.first<i) return i; for (int i=0;i<G[x.second].size();i++) { int y=G[x.second][i]; if (--deg[y]==0) q.push(make_pair(k[y],y)); } } return n; } int main() { cin>>n>>m; for (int i=1;i<=n;i++) scanf("%d",&k[i]); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); G[y].push_back(x); deg[x]++; } memcpy(rc,deg,sizeof(deg)); toposort(); for (int i=1;i<=n;i++) printf("%d ",solve(i)); return 0; }