拓扑排序+不是字典序的优先级排列(POJ3687+HDU4857)
一、前言
在过去的一周里结束了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)。这种方法从玄学上保证了输出“依照题目意思有序”。
二、思路和相关优化
思路简单的讲就是拓扑排序过程中,通过检测是否有新的元素已经可以被当做随时可以加入队列的元素,如果有,就加入优先队列,如果没有就继续。
一般来说,使用字典序输出拓扑排序是一件很简单的事情。对比了网上其他人写的代码,我们可以直观的认为至少有如下几种优化方式:
- 使用邻接表来存储具体的边信息而不是邻接矩阵。(可以证明,使用vector作为邻接表插入N条边的时间期望应当是O(N))
- 使用优先队列、multiset来从集合中选取最大最小的元素
- 使用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)的更多相关文章
- POJ 1128 Frame Stacking(拓扑排序·打印字典序)
题意 给你一些矩形框堆叠后的鸟瞰图 推断这些矩形框的堆叠顺序 每一个矩形框满足每边都至少有一个点可见 输入保证至少有一个解 按字典序输出全部可行解 和上一题有点像 仅仅是这个要打印全部的可行 ...
- 【拓扑排序】CDOJ1635 琵琶弦上说相思,当时明月在,曾照彩云归
对于两个相邻的字符串 Si和Si+1 ,如果它们的前k-1位都相同,第k位不相同,那么,在字典序中 Si,k一定在 Si+1,k前面 建立有向边从 Si,k到 Si+1,k,进行拓扑排序 为了保证字典 ...
- POJ--1094--Sorting It All Out||NYOJ--349--Sorting It All Out(拓扑排序)
NYOJ的数据水一点,POJ过了是真的过了 /* 拓扑排序模板题: 每次输入都要判断有环与有序的情况,如果存在环路或者已经有序可以输出则跳过下面的输入 判断有序,通过是否在一个以上的入度为0的点,存在 ...
- POJ3687拓扑排序+贪心
题意: 给你n个求,他们的重量是1-n(并不是说1号求的重量是1...),然后给你m组关系a,b,表示a的重量小于b的重量,然后让你输出满足要求的前提下每个球的重量,要求字典序最小. 思路 ...
- 现有‘abcdefghijkl’12个字符,将其所有的排列按字典序进行排序,给出任意一组排列,说出这租排列在所有排列中是第几小的
题目: 现有‘abcdefghijkl’12个字符,将其所有的排列按字典序进行排序,给出任意一组排列,说出这租排列在所有排列中是第几小的 据说这道题是百度校招的一道算法题,反正我觉得我在学校的时候很可 ...
- 拓扑排序详解(梅开二度之dfs版按字典序输出拓扑路径+dfs版输出全部拓扑路径
什么是拓扑排序? 先穿袜子再穿鞋,先当孙子再当爷.这就是拓扑排序! 拓扑排序说白了其实不太算是一种排序算法,但又像是一种排序(我是不是说了个废话qwq) 他其实是一个有向无环图(DAG, Direct ...
- uoj#278. 【UTR #2】题目排列顺序(拓扑排序)
传送门 对于每一个位置\(i\)来说,上一个和它的\(f_i\)相同的点一定比它大,我们从上一个\(f_i\)和它相同的点向它连边.第一个\(f_i-1\)出现的位置一定比它小,把它向那个位置连边. ...
- HDU 4857 逃生 【拓扑排序+反向建图+优先队列】
逃生 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission ...
- hdu 1285 确定比赛名次 拓扑排序
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1285 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛 ...
随机推荐
- 在 Angularjs 中 ui-sref 和 $state.go 如何传递单个多个参数和将对象作为参数
一: 如何传递单个参数 首先,要在目标页面定义接受的参数: 传参, 接收参数, 在目标页面的controller里注入$stateParams,然后 "$stateParams.参数名&qu ...
- JQuery notepad
ready:在文档加载后执行,在文档对象加载完毕后,页面完全显示后执行,把所有事件函数放在ready中加载是一种非常好的方法,ready() 函数不应与 <body onload="& ...
- C#工程缺少IIS组件无法打开的解决办法
作者:朱金灿 来源:http://blog.csdn.net/clever101 同事使用VS打开一个C#工程,出现下面的错误: 这个工程是C#的桌面工程,跟IIS无关,去安装IIS太麻烦了.我想到一 ...
- Cocos2d-x v3.1 Hello world程序(四)
Cocos2d-x v3.1 Hello world程序(四) 在上一篇文章中我们我们已经使用Cocos-Console工具生成了工程,本机生成的目录为:"D:\CocosProject\T ...
- 【extjs6学习笔记】0.4 准备: 书籍与文档
Ext JS 6 By Example Ext JS Essentials Learning Ext JS - Fourth Edition Ext JS 6: Getting Started htt ...
- /usr/local/sbin/arpspoof
/usr/local/sbin/arpspoof arpspoof -t 攻击者ip地址 网关ip地址 稍等系,被攻击者机器的arp的缓存就已经变了.
- javascript的Array.prototype.map()和jQuery的jQuery.map()
两个方法都可以根据现有数组创建新数组,但在使用过程中发现有些不同之处 以下面这个数据为例: var numbers = [1, 3, 4, 6, 9]; 1. 对undefined和null的处理 a ...
- COGS 11. 运输问题1
★★☆ 输入文件:maxflowa.in 输出文件:maxflowa.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 一个工厂每天生产若干商品,需运输到 ...
- Python风格规范-FYI
Python风格规范 分号 Tip 不要在行尾加分号, 也不要用分号将两条命令放在同一行. 行长度 Tip 每行不超过80个字符 例外: 长的导入模块语句 注释里的URL 不要使用反斜杠连接行. Py ...
- Java jvm 内存回收机制
http://blog.csdn.net/yaerfeng/article/details/51291903 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方 ...