Java (javac)
时间限制
600 ms
内存限制
128 MB
Python (python3)
时间限制
500 ms
内存限制
64 MB
其他编译器
时间限制
200 ms
内存限制
题目描述:
人类喜欢用 10 进制,大概是因为人类有一双手 10 根手指用于计数。于是在千手观音的世界里,数字都是 10 000 进制的,因为每位观音有 1 000 双手 ……
千手观音们的每一根手指都对应一个符号(但是观音世界里的符号太难画了,我们暂且用小写英文字母串来代表),就好像人类用自己的 10 根手指对应 0 到 9 这 10 个数字。同样的,就像人类把这 10 个数字排列起来表示更大的数字一样,ta们也把这些名字排列起来表示更大的数字,并且也遵循左边高位右边低位的规则,相邻名字间用一个点 . 分隔,例如 pat.pta.cn 表示千手观音世界里的一个 3 位数。
人类不知道这些符号代表的数字的大小。不过幸运的是,人类发现了千手观音们留下的一串数字,并且有理由相信,这串数字是从小到大有序的!于是你的任务来了:请你根据这串有序的数字,推导出千手观音每只手代表的符号的相对顺序。
注意:有可能无法根据这串数字得到全部的顺序,你只要尽量推出能得到的结果就好了。当若干根手指之间的相对顺序无法确定时,就暂且按它们的英文字典序升序排列。例如给定下面几个数字:
pat
cn
lao.cn
lao.oms
pta.lao
pta.pat
cn.pat
我们首先可以根据前两个数字推断 pat < cn;根据左边高位的顺序可以推断 lao < pta < cn;再根据高位相等时低位的顺序,可以推断出 cn < oms,lao < pat。综上我们得到两种可能的顺序:lao < pat < pta < cn < oms;或者 lao < pta < pat < cn < oms,即 pat 和 pta 之间的相对顺序无法确定,这时我们按字典序排列,得到 lao < pat < pta < cn < oms。
输入格式:
输入第一行给出一个正整数 N (≤
1
0
5
10^{5}
105 ),为千手观音留下的数字的个数。随后 N 行,每行给出一个千手观音留下的数字,不超过 10 位数,每一位的符号用不超过 3 个小写英文字母表示,相邻两符号之间用 . 分隔。
我们假设给出的数字顺序在千手观音的世界里是严格递增的。题目保证数字是 1 0 4 10^{4} 104 进制的,即符号的种类肯定不超过 1 0 4 10^{4} 104 种。
输出格式:
在一行中按大小递增序输出符号。当若干根手指之间的相对顺序无法确定时,按它们的英文字典序升序排列。符号间仍然用 . 分隔。
输入样例:
7
pat
cn
lao.cn
lao.oms
pta.lao
pta.pat
cn.pat
输出样例:
lao.pat.pta.cn.oms
给定n行字符串
以 “.” 分割开字符串为 x 位
n行总共有 k 个 以 “.” 分割开来的字符串
求出 k进制 从小到大排序
不确定的按它们的字典序升序排列
emmmmmmm
因为题目给定的是 n 条式子是从小到大的关系 也就是 X 1 < X 2 < . . . < X n X_{1} \lt X_{2} \lt ... \lt X_{n} X1<X2<...<Xn 这 n 行式子
因为每行式子以 “.” 分隔开来 所以每行就有
x
1
、
x
2
、
.
.
.
、
x
n
x_{1}、 x_{2}、 ...、 x_{n}
x1、x2、...、xn 个数字
那么一共就有 K 种 “数字” 也就是
∑
i
=
1
n
x
i
=
K
\sum_{i = 1} ^ {n} x_{i} = K
∑i=1nxi=K
题目需要我们求出这K个数字 之间的大小关系 输出按从小到大输出
我们可以从
X
1
<
X
2
<
.
.
.
<
X
n
X_{1} \lt X_{2} \lt ... \lt X_{n}
X1<X2<...<Xn中 能大致推出 k 个数字之间的大小关系
因为存在位数不确定的情况
所以题目中有 > 当若干根手指之间的相对顺序无法确定时,就暂且按它们的英文字典序升序排列 <
综上所述 本题可以推出是个拓扑排序
先说说如何去比较两个点 也就找出一条边
首先位数相同, 因为位数要是不同的话, 那么位数长的数一定就是最大的
也就是
a
.
b
.
c
a.b.c
a.b.c 一定大于
d
.
e
d.e
d.e
无论
a
,
b
,
c
,
d
,
e
{a,b,c,d,e}
a,b,c,d,e取任何数都是
a
.
b
.
c
>
d
.
e
a.b.c > d.e
a.b.c>d.e
所以需要位数相同
那么位数相同的时候呢
因为给定的n个式子是严格上升的
所以只需要找出第一个不相等的位数 将他们两个联立即可 也就是一条边
最后的最后也就是按照拓扑排序的方法去添加边
如果相邻两条式子要是位数相同的话
那么我们就去找第一个相同的数
给这两个数加上一条边为 上面式子的数指向下面式子的数
然后入度 +1
最后边添加完毕后 直接求出字典序最小的拓扑排序即可
很无奈 我的快读以及快出不是很快 所以我的java究极TLE
import java.io.*;
import java.math.*;
import java.util.*;
public class Main
{
static int N = (int) 1e5 << 1;
static TreeMap<String, Integer> h = new TreeMap<String, Integer>();
static String shu[] = new String[N + 10];
static int ne[] = new int[N + 10], idx = 1;
static TreeMap<String, Integer> d = new TreeMap<String, Integer>();
static String[] get(String str)
{
String s[] = str.split("\\.");
for (String i : s)
{
if (!h.containsKey(i)) h.put(i, 0);
if (!d.containsKey(i)) d.put(i, 0);
}
return s;
}
// A -> B
static void add(String A, String B)
{
shu[idx] = B;
ne[idx] = h.get(A);
h.put(A, idx++);
}
static ArrayList<String> topusort()
{
// 顺序不确定时,按它们的英文字典序升序排列
PriorityQueue<String> q = new PriorityQueue<String>();
for (String s : d.keySet())
{
if (d.get(s) == 0)
q.add(s);
}
ArrayList<String> res = new ArrayList<String>();
while (q.size() != 0)
{
String u = q.poll();
res.add(u);
for (int i = h.get(u); i != 0; i = ne[i])
{
String j = shu[i];
d.merge(j, -1, Integer::sum);
if (d.get(j) == 0)
q.add(j);
}
}
return res;
}
public static void main(String[] args)
{
int n = sc.nextInt();
String str = sc.next(); String last[] = get(str);
for (int i = 2; i <= n; i++)
{
str = sc.next(); String now[] = get(str);
// 因为题目给定是从小到大的顺序排列 所以只需要比较相邻两个即可
// 所以 如果位数相同的话 才能好比较第一个不相同的位数是那个
if (now.length == last.length)
{
for (int j = 0; j < now.length; j++)
{
// 找到第一个不同的位置
if (!now[j].equals(last[j]))
{
// 所以 last[j] < now[j] => last[j] -> now[j] 有一条边
add(last[j], now[j]);
// now[j]也就是B, B的入度 +1
d.merge(now[j], 1, Integer::sum);
break;
}
}
}
last = now;
}
ArrayList<String> res = topusort();
for (int i = 0; i < res.size(); i++)
{
if (i != 0) out.print(".");
out.print(res.get(i));
}
out.flush();
out.close();
}
static Scanner sc = new Scanner(System.in);
static PrintWriter out = new PrintWriter(System.out);
}
c++
#include <iostream>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5;
unordered_map<string, int> h;
string shu[N + 10];
int ne[N + 10], idx = 1;
unordered_map<string, int> d;
vector<string> to(string s)
{
vector<string> res;
string t;
for(auto i : s)
{
if(i == '.')
{
if(!d.count(t)) d[t] = 0;
if(!h.count(t)) h[t] = 0;
res.push_back(t);
t = "";
}
else
t += i;
}
if(!d.count(t)) d[t] = 0;
if(!h.count(t)) h[t] = 0;
res.push_back(t);
return res;
}
void add(string a, string b)
{
shu[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
vector<string> topusort()
{
priority_queue<string, vector<string>, greater<string>> q;
for (auto i : d)
{
if (i.second == 0)
q.push(i.first);
}
vector<string> res;
while (q.size() != 0)
{
string s = q.top(); q.pop();
res.push_back(s);
for (int i = h[s]; i != 0; i = ne[i])
{
string j = shu[i];
if (-- d[j] == 0)
q.push(j);
}
}
return res;
}
int main()
{
int n; scanf("%d", &n);
char str[10 * 3 + 10 - 1 + 10];
scanf("%s", str);
auto last = to(str);
for (int i = 2; i <= n; i++)
{
scanf("%s", str);
auto now = to(str);
if (now.size() == last.size())
{
for (int j = 0; j < now.size(); j++)
{
if (now[j] != last[j])
{
add(last[j], now[j]);
d[now[j]] ++;
break;
}
}
}
last = now;
}
auto res = topusort();
for (int i = 0; i < res.size(); i++)
{
if (i != 0)
printf(".");
printf("%s", res[i].c_str());
}
return 0;
}
如果有说错的 或者 不懂的 尽管提 嘻嘻
一起进步!!!