一、前言

在过去的一周里结束了CCSP的比赛,其中有一道题卡了我9个小时,各种调错都没法完整的调处来这题,于是痛下决心开始补题,这个是计划的一部分。事实上,基于错误的理解我写了若干发拓扑排序+字典序的算法,但是集体统一GG,最后发现,实际上要求设计的并不是严格意义上的最小字典序,而是“最小的必然放在最大的之前”这种看上去很类似但是时至完全不一样的说法。而这也是为什么,正想建树GG但是反向建树,用大顶堆来找最大的思路是正确的。这实际上等价于,“寻找最大字典序并且反向输出”这个过程。

首先看一组样例

1

3 1

3 1
        对于改组样例,有约束——3必须在1前面,因为如果有最小字典序正想输出的算法就会得到2 3 1。但是这个数据明显的违反了题目对于顺序的规约——“如果存在一个1,能够在2前面,那么就必须把1放到2前面,在这之后,如果还有2能够放在3前面,就必须把2放到3前面”。于是我们直觉上认为,这种说法其实等价于,首先把所有可能的最大值全放到最后,用以保证不会有任何一个合法的小数放到大数的后面。正确的做法是,2 1 3(逆向输出是3,1,2)。这种方法从玄学上保证了输出“依照题目意思有序”。

二、思路和相关优化

        思路简单的讲就是拓扑排序过程中,通过检测是否有新的元素已经可以被当做随时可以加入队列的元素,如果有,就加入优先队列,如果没有就继续。

        一般来说,使用字典序输出拓扑排序是一件很简单的事情。对比了网上其他人写的代码,我们可以直观的认为至少有如下几种优化方式:

  1. 使用邻接表来存储具体的边信息而不是邻接矩阵。(可以证明,使用vector作为邻接表插入N条边的时间期望应当是O(N))
  2. 使用优先队列、multiset来从集合中选取最大最小的元素
  3. 使用CNT[]数组来记录该点被指向的次数,之后在topsort当中通过对cnt数组相应元素的判断来确定这个值是不是等于零

三、通用AC代码

POJ3687

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<set>
#include<vector>
#include<queue>
using namespace std; const long long MAXN=; vector<int>G[MAXN];
int cnt[MAXN];
long long n,m; int vis[MAXN];
bool dfs(int now)
{
vis[now]=;
int len=G[now].size();
for(int i=;i<len;++i)
{
int tar=G[now][i];
if(vis[tar]==)return true;
if(vis[tar]==&&dfs(tar))return true; }vis[now]=;
return false;
}
bool check_circle()
{
memset(vis,,sizeof(int)*(n+));
for(int i=;i<=n;++i)
{
if(vis[i]==&&dfs(i))return true;
}return false;
}
int ans[MAXN];
void topSort()
{
priority_queue<int>q;
int summ=n;
for(int i=;i<=n;++i)
{
if(cnt[i]==)q.push(i);
}
while(!q.empty())
{
int now=q.top();q.pop();
int len=G[now].size();
ans[now]=summ--;
for(int i=;i<len;++i)
{
int tar=G[now][i];
cnt[tar]--;
if(cnt[tar]==)q.push(tar);
}
}
} void init()
{
memset(cnt,,sizeof(int)*n+);
cin>>n>>m;
for(int i=;i<=n;++i)
{
G[i].clear();
}
for(int i=;i<m;++i)
{
int a,b;
cin>>a>>b;
G[b].push_back(a);
cnt[a]++;
}
if(check_circle())
{
cout<<"-1\n";
return ;
}
topSort();
for(int i=;i<=n;++i)
{
cout<<ans[i]<<" ";
}cout<<endl;
} int main()
{
cin.sync_with_stdio(false);
int ca;
cin>>ca;
while(ca--)init(); return ;
}

HDU4857

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<set>
#include<vector>
#include<queue>
using namespace std; const long long MAXN=; vector<int>G[MAXN];
int cnt[MAXN];
long long n,m; int vis[MAXN];
bool dfs(int now)
{
vis[now]=;
int len=G[now].size();
for(int i=;i<len;++i)
{
int tar=G[now][i];
if(vis[tar]==)return true;
if(vis[tar]==&&dfs(tar))return true; }vis[now]=;
return false;
}
bool check_circle()
{
memset(vis,,sizeof(int)*(n+));
for(int i=;i<=n;++i)
{
if(vis[i]==&&dfs(i))return true;
}return false;
}
int ans[MAXN];
void topSort()
{
priority_queue<int>q;
int summ=n;
for(int i=;i<=n;++i)
{
if(cnt[i]==)q.push(i);
}
while(!q.empty())
{
int now=q.top();q.pop();
int len=G[now].size();
ans[summ--]=now;
for(int i=;i<len;++i)
{
int tar=G[now][i];
cnt[tar]--;
if(cnt[tar]==)q.push(tar);
}
}
} void init()
{
memset(cnt,,sizeof(int)*n+);
cin>>n>>m;
for(int i=;i<=n;++i)
{
G[i].clear();
}
for(int i=;i<m;++i)
{
int a,b;
cin>>a>>b;
G[b].push_back(a);
cnt[a]++;
}
if(check_circle())
{
cout<<"-1\n";
return ;
}
topSort();
for(int i=;i<n;++i)
{
cout<<ans[i]<<" ";
}cout<<ans[n]<<endl;
} int main()
{
cin.sync_with_stdio(false);
int ca;
cin>>ca;
while(ca--)init(); return ;
}

拓扑排序+不是字典序的优先级排列(POJ3687+HDU4857)的更多相关文章

  1. POJ 1128 Frame Stacking(拓扑排序&#183;打印字典序)

    题意  给你一些矩形框堆叠后的鸟瞰图  推断这些矩形框的堆叠顺序  每一个矩形框满足每边都至少有一个点可见  输入保证至少有一个解 按字典序输出全部可行解 和上一题有点像  仅仅是这个要打印全部的可行 ...

  2. 【拓扑排序】CDOJ1635 琵琶弦上说相思,当时明月在,曾照彩云归

    对于两个相邻的字符串 Si和Si+1 ,如果它们的前k-1位都相同,第k位不相同,那么,在字典序中 Si,k一定在 Si+1,k前面 建立有向边从 Si,k到 Si+1,k,进行拓扑排序 为了保证字典 ...

  3. POJ--1094--Sorting It All Out||NYOJ--349--Sorting It All Out(拓扑排序)

    NYOJ的数据水一点,POJ过了是真的过了 /* 拓扑排序模板题: 每次输入都要判断有环与有序的情况,如果存在环路或者已经有序可以输出则跳过下面的输入 判断有序,通过是否在一个以上的入度为0的点,存在 ...

  4. POJ3687拓扑排序+贪心

    题意:       给你n个求,他们的重量是1-n(并不是说1号求的重量是1...),然后给你m组关系a,b,表示a的重量小于b的重量,然后让你输出满足要求的前提下每个球的重量,要求字典序最小. 思路 ...

  5. 现有‘abcdefghijkl’12个字符,将其所有的排列按字典序进行排序,给出任意一组排列,说出这租排列在所有排列中是第几小的

    题目: 现有‘abcdefghijkl’12个字符,将其所有的排列按字典序进行排序,给出任意一组排列,说出这租排列在所有排列中是第几小的 据说这道题是百度校招的一道算法题,反正我觉得我在学校的时候很可 ...

  6. 拓扑排序详解(梅开二度之dfs版按字典序输出拓扑路径+dfs版输出全部拓扑路径

    什么是拓扑排序? 先穿袜子再穿鞋,先当孙子再当爷.这就是拓扑排序! 拓扑排序说白了其实不太算是一种排序算法,但又像是一种排序(我是不是说了个废话qwq) 他其实是一个有向无环图(DAG, Direct ...

  7. uoj#278. 【UTR #2】题目排列顺序(拓扑排序)

    传送门 对于每一个位置\(i\)来说,上一个和它的\(f_i\)相同的点一定比它大,我们从上一个\(f_i\)和它相同的点向它连边.第一个\(f_i-1\)出现的位置一定比它小,把它向那个位置连边. ...

  8. HDU 4857 逃生 【拓扑排序+反向建图+优先队列】

    逃生 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission ...

  9. hdu 1285 确定比赛名次 拓扑排序

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1285 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛 ...

随机推荐

  1. asp.net MVC 4.0 Model元数据回顾——HtmlHelper的ModelMetadata

    模板方法包括Display/DisplayFor.Editor/EditorFor.DisplayForModel/EditForModel提供辅助生成Html的模型元数据信息 public stat ...

  2. mysql通用分页存储过程遇到的问题

    DELIMITER $$ USE `tsb_asksys`$$ DROP PROCEDURE IF EXISTS `P_viewPage`$$ CREATE DEFINER=`root`@`local ...

  3. erlang通讯解析浮点数的一些问题

    这两天我弟弟遇到一个比较有意思的问题.他在前端协定数据协议的时候用到了float和double,所以他想问float和double在erlang后端中是如何解析的.我之前写协议也很少用到浮点数,所以也 ...

  4. VMware 虚拟机安装及部署

    Linux系统安装及网络配置 这篇文章介绍关于Linux系统的安装以及网络配置,关于虚拟机配置中网络的几个模式区别进行详细讲解.学习Linux对于后端开发人员来说是很有必要的,结合实际开发,Linux ...

  5. Catch the moments of your life. Catch them while you're young and quick.

    Catch the moments of your life. Catch them while you're young and quick.趁你还年轻利落,把握住生活中的美好瞬间吧!

  6. Android应用瘦身

    转:https://zhuanlan.zhihu.com/p/25465537 瘦身的目的 从目的导向来看,我们是不会无缘无故去做一件事情的,那我们对应用瘦身的目的是为了什么?答案是:提高下载转化率. ...

  7. Neo4j-3.0.3 (Debian 8)

    平台: Ubuntu 类型: 虚拟机镜像 软件包: neo4j-3.0.3 basic software database graph database infrastructure neo4j op ...

  8. jQuery中常用的元素查找方法总结

    $("#myELement") 选择id值等于myElement的元素,id值不能重复在文档中只能有一个id值是myElement所以得到的是唯一的元素 $("div&q ...

  9. scrum敏捷开发☞

    scrum是什么?说白了就是自带一些规则的工具,团队成员按照scrum的规则计划项目,执行项目,完成项目..可以让团队提高工作效率 当前除了scrum还有其他很多类似的像Kanban,XP,RUP(规 ...

  10. java中list强转为map类型

    起因:读取数据库文件的测试用例,测试用例需要存放到一个map中,方便下次调用, 读取的内容返回的内容存放在一个list中,并且数据内容是key=value的形式,最开始使用切片方式,做了很多无用功,后 ...