[题目]拓扑排序

时光怂恿深爱的人放手 提交于 2019-12-04 06:19:09

拓扑排序

P3243 [HNOI2015]菜肴制作:注意到题面要求先1尽量靠前,然后2尽量靠前,然后。。。
其实是贪心,需要建反图然后拿大根堆去跑,原因:我们先从可能成为最后的点中挑一个最大的出来(肯定是不劣的),然后把他的所有后继节点插入,此时我们接着取最大的;这样我们相当于先取能用的点中最大的点,尽量后取更小的点,所以我们能够把更小的点尽量向前放。

inline void main() {
    T=g(); while(T--) { cnt=tot=0;
        priority_queue<int> q;
        memset(fir,0,sizeof fir),memset(in,0,sizeof in);
        n=g(),m=g(); for(R i=1,u,v;i<=m;++i) u=g(),v=g(),add(v,u);
        for(R i=1;i<=n;++i) if(!in[i]) q.push(i);
        while(q.size()) { R u=q.top(); q.pop(); ans[++tot]=u;
            for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
                if(--in[v]==0) q.push(v);
            }
        } if(tot<n) {puts("Impossible!"); continue;}
        for(R i=n;i;--i) printf("%d ",ans[i]); puts("");
    }
}

P1954 [NOI2010]航空管制 :类似上一道题。注意第一问仍然不能建正图,因为有可能类似这样:

这样你就把 \(k_i=2\) 的那个点咕掉了。。
所以就要建反图:因为建反图后转化为在拓扑序上的位置 \(pos_i>n-k_i\) ,这样我们一直取出最小的就一定正确。(OTZ)
这样我们就可以解决第一问。
然后对于第二问,我们要对于每个点跑一边\(\mathcal{O}(mlogn)\) 的判定。具体的,跑反图,我们求出每个点能够放进去的最晚时间,即在原图中的最早时间。

inline int solve(int x) {//返回x点最早的时间。
    R tot=0; while(q.size()) q.pop();
    memcpy(in,mem,sizeof mem);
    for(R i=1;i<=n;++i) if(i!=x&&!in[i]) q.push(mp(c[i],i));
    while(q.size()) { R w=q.top().first,u=q.top().second; q.pop();
        if(w<n-tot) return n-tot;
        for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
            --in[v]; if(!in[v]&&x!=v) q.push(mp(c[v],v));
        }
        ++tot;
    } return n-tot;
}

P3588 [POI2015]PUS:好题,线段树优化建图+拓扑DP or 差分约束(都差不多),类似差分约束的模型,\(a<b\rightarrow a\leq b-1 \rightarrow b\)\(a\) 连一条权值为 \(-1\) 的边,跑类似最短路的DP。还是想一下之前的总结,我们发现,边权是非正的,并且0边没有形成环,所以一旦有环一定是负环,原问题无解。所以我们可以用拓扑排序取DP解这道题。\(d[v]=min(d[v],d[u]+w[i])\),我们将所有没有确定的值均设成 \(1e9\) (题目中要求的上界),按照DP的式子,我们跑出来的一定是每个点的最大值(经过最少的边),若我们发现确定的值被更新的更小了,也是无解。否则有解,直接输出 \(d\) 数组即可。

P5603 小C与桌游:贪心,拓扑排序。
第一问我们一直取出最小的即可。第二问对于当前的点,扩展所有能扩展的更小的点即可。

const int N=500010;
int n,m,ans1,ans2,cnt,mx;
int vr[N],nxt[N],fir[N],mem[N],in[N],s[N],tot; 
priority_queue<int> p;
priority_queue<int,vector<int>,greater<int> > q;
inline void add(int u,int v) {vr[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;}
inline void main() {
    n=g(),m=g(); for(R i=1,u,v;i<=m;++i) u=g(),v=g(),add(u,v),++mem[v];
    memcpy(in,mem,sizeof mem);
    for(R i=1;i<=n;++i) if(!in[i]) q.push(i); 
    while(q.size()) { R u=q.top(); q.pop();
        if(u>mx) ++ans1,mx=u;
        for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
            if(--in[v]==0) q.push(v);
        }
    } memcpy(in,mem,sizeof mem),mx=0;
    for(R i=1;i<=n;++i) if(!in[i]) p.push(i); 
    while(p.size()) { R u=p.top();
        if(u>mx) ++ans2,mx=u;
        tot=0; while(p.size()) s[++tot]=p.top(),p.pop();
        for(R i=1;i<=tot;++i) { R u=s[i]; 
            for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
                if(--in[v]==0) {
                    if(v>mx) p.push(v);
                    else s[++tot]=v;
                }
            }
        }
    } printf("%d\n%d\n",ans1,ans2);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!