• P3243 [HNOI2015]菜肴制作


    萌新今天刚刚接触拓扑排序,找到了一些题,然后就来做这道题紫题了(雾)

    还是不要被题目的难度吓到吧,其实这道题挺模板的,思路感觉也比较好想,但是关于证明这些,我太菜,并不会。是一道非常好的入手拓扑排序的题,不像其他题目那样难想

    对于一个限制 <i,j>,其实表示的就是 i 必须在 j 之前完成,我们把这个条件转化成图的形式就是 i 到 j 有一条有向边,然后就可以试着往拓扑排序的方向思考了

    拓扑排序最基础的应用是什么,可以把它当做学习一个东西(比如语文吧),你要学习一篇文章,你总得先学会认字吧,学会认字肯定就要学会拼音,那么你要提前学习的东西就是前置知识,就相当于以上提到的 j (你要完成 j ,你必须先学会 i )。那这么一看,裸裸的拓扑排序

    但是有一点变化的是什么呢?题目中的编号越小,表示它的预估质量越高(显而易见,这里指的不是字典序),所以对于编号小的,我们希望它在前面,而编号的就往后面放

    因为如果正序按题目建边的话,假如1号点的入度不为1,而2的入度为0,我们应该先把限制1的点全部删掉,再把1弄出来,再去删2,这样才能保证最优,但是正序直接建立的话我们会把2直接删去

    所以我们应该倒序建立。我们可以通过大根堆来实现把编号越大的越先处理,然后再倒序输出答案就可以了(可以理解为先把价值低的存储下来,价值高的就在后面,这个时候倒序输出就可以保证价值越高的越先做)

    然后剩下了一个 Impossible ,这个也很简单,拓扑排序的另外一种应用,判断是否有环存在就可以了

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=500000;
    int d;
    int n,m;
    int head[MAXN],tot;
    struct node{
    	int net,to;
    }e[MAXN];
    void add(int x,int y){
    	e[++tot].to=y;
    	e[tot].net=head[x];
    	head[x]=tot;
    } //链式前向星存储边,邻接矩阵一般来说会慢一点 
    int ru[MAXN]; //入度 
    int ans[MAXN],now; //存储答案 
    bool toop(){  //直接写成bool类型容易判断一些 
    	priority_queue<int>q; //堆(优先队列) STL确实很方便 
    	for(register int i=1;i<=n;i++){
    		if(ru[i]==0) q.push(i); //入度为0的加入堆 
    	}
    	while(!q.empty()){
    		int x=q.top();
    		q.pop();
    		ans[++now]=x; //存储答案 
    		for(register int i=head[x];i;i=e[i].net){
    			ru[e[i].to]--; //这个点删去了,它能到的点的入度就要-- 
    			if(ru[e[i].to]==0){ //入度为0的加入堆中 
    				q.push(e[i].to);
    			} 
    		}
    	}
    	if(now==n) return true; //如果刚好处理了n个,说明没有环 
    	return false; //否则有环,出错 
    }
    int main(){
    	scanf("%d",&d);
    	while(d--){
    		now=0,tot=0;
    		for(register int i=1;i<=n;i++) ru[i]=0,ans[i]=0;
    		memset(e,0,sizeof e);
    		memset(head,0,sizeof head); //注意清空数组,建议大家自己如果能手动清空就别用memset 
    		scanf("%d%d",&n,&m);
    		for(register int i=1;i<=m;i++){
    			int x,y;
    			scanf("%d%d",&x,&y);
    			add(y,x);
    			ru[x]++; //灵魂的反向建边 
    		}
    		//bool k=toop(); 
    		//这个地方挺玄学的吧,我最开始声明了一个新变量判断是否有环(如上) 
    		//然后它就超时了,所以这里直接写成toop()直接判断就可以了 
    		if(toop()==false) puts("Impossible!"); //如果有环就不可能 
    		else{
    			for(register int i=now;i>=1;i--) printf("%d ",ans[i]); //倒序输出答案 
    			puts("");
    		}
    	}
    	return 0;
    }
    

    关于拓扑排序的一些具体介绍,大家可以去看看 洛谷日报 快速入手拓扑排序这不会被算作抄袭吧) ,然后宣传一下自己同桌的博客

  • 相关阅读:
    php用redis保存session
    php字符串常用算法--字符串加密解密
    PHP file_get_contents设置超时处理方法
    php栈数据结构和括号匹配算法
    JavaScript 函数参数传递到底是值传递还是引用传递
    JavaScript数据操作--原始值和引用值的操作本质
    JavaScript数据类型--值类型和引用类型
    js基本数据类型和typeof
    JS获取字符串实际长度(包含汉字)
    sublime的一些插件
  • 原文地址:https://www.cnblogs.com/Poetic-Rain/p/13215684.html
Copyright © 2020-2023  润新知