L3-031 千手观音 - java

L3-031 千手观音


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} x1x2...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;
}

拓扑排序
拓扑排序
拓扑排序
在这里插入图片描述


如果有说错的 或者 不懂的 尽管提 嘻嘻

一起进步!!!


闪现

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢谢 啊sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值