问题描述

通过最近的一次核酸检测,疫情防控小组检测到了若干个阳性人员,通过调取行程码数据,防控小组获取到了这些病例曾经去过的地点。现在希望对这些地点做源头可能性分析,来找出哪些地点更有可能是本次疫情的源头。
对于一个病例,如果其曾到过a,b,c三个地点,那么这三个地点都会在其源头可能得分上增加1/3,如果某个病例曾经去过a,c两个地点,那么这两个地点作为源头的得分都会增加1/2,如果只去过一个地点的病例,那么该地点作为源头的概率很高,我们给这个地点的得分增加1。
简而言之,如果一个病例去过n个地点,那么这n个地点都会得到1/n的得分。现在给你所有病例去过的地点的情况,你能否分析出每个地点作为源头的可能性大小顺序?

输入格式:

第一行一个整数n(1≤_n_≤1000),表示记录的条数
其后n行,每行两个不为空的字符串a,b(1≤∣_a_∣,∣_b_∣≤10),仅包含小写字母。其中a代表病例的名字,b代表地点。
注意:当出现有人多次去同一地点时,只计算一次

输出格式:

输出若干行,每行代表一个地点名,代表所有地点作为源头的得分从高到低排序的结果。如果有多个地点得分相同,地点名字典序小的优先。

输入样例:

6
xiaoa
adian
xiaob
adian
xiaob
bdian
xiaoc
adian
xiaoc
bdian
xiaoc
cdian

输出样例:

adian
bdian
cdian


代码长度限制
16 KB
时间限制
1000 ms
内存限制
64 MB


参考用例

输入 输出 备注
4
x 5
y 0
z 1
z 5
5
0
1
此用例
5地点 1+0.5
0地点 1
1地点 0.5
5
x 5
x 5
y 0
z 1
z 5
5
0
1
一个人去两个地方情况
3
z 6
x 1
y 5
1
5
6
考虑字典排序

代码一 无脑解决

import java.util.*;
public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();TreeSet<String> treeSet=new TreeSet<>();sc.nextLine();for(int i=0;i<n;i++){String str=sc.nextLine();treeSet.add(str);//用来去出同一个人去同一个地方}TreeMap<String,Double> map=new TreeMap<>();String a[]=new String[treeSet.size()];//记录人String b[]=new String[treeSet.size()];//记录人去的地方int t=0;TreeSet<String> treeSet1=new TreeSet<>();TreeSet<String> treeSet2=new TreeSet<>();for(String i :treeSet){String sb[]=i.split(" ");a[t]=sb[0];b[t]=sb[1];treeSet1.add(a[t]);map.put(b[t],0.0);t++;}for(String i :treeSet1){double cnt=0;for(int j=0;j<t;j++){if(i.equals(a[j])){cnt++;}}for(int j=0;j<t;j++){if(i.equals(a[j])){map.put(b[j],map.get(b[j])+(1/cnt));}}}//进行排序对mapSet<String> strings = map.keySet();TreeSet<String>  treeSet3= new TreeSet(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {double t=  (map.get(o2)-map.get(o1));if(t>=0)return 1;return -1;}});treeSet3.addAll(strings);for(String i :treeSet3){System.out.println(i);}}}

代码二 集合方式解决

import java.util.*;
/*** @author 必燃* @version 1.0* @create 2022-05-13 19:35 //开始日期*/
public class 感染源在哪里 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();//人和地址集合HashMap<Person, HashSet<String>> map = new HashMap();//地址集合HashMap<String, Double> admap = new HashMap();//数据预处理for (int i = 0; i < n; i++) {if(i==0){sc.nextLine();}String s = sc.nextLine();String[] ss = s.split(" ");admap.put(ss[1], 0.000);//System.out.println(admap.get(ss[1]));Person p = new Person(ss[0]);if (map.get(p) == null) {map.put(p, new HashSet());}//若加入else会导致第一个消失。map.get(p).add(ss[1]);}Set<Person> set = map.keySet();for (Person p :set) {p.cnt = map.get(p).size();for (String ad :map.get(p)) {if (admap.containsKey(ad)) {admap.put(ad, admap.get(ad) + (1.0 / p.cnt));
//                    System.out.println(1/p.cnt);
//                    System.out.println(admap.get(ad));}}}List<Map.Entry<String, Double>> list = new ArrayList<Map.Entry<String, Double>>(admap.entrySet());Collections.sort(list, new Comparator<Map.Entry<String, Double>>() {@Overridepublic int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {int t =  (int)((o2.getValue())*1000 - (o1.getValue()*1000));if(t==0){return o1.getKey().compareTo(o2.getKey());}return t;
//                if (o2.getValue().equals(o1.getValue()) ) {//                    return o1.getKey().compareTo(o2.getKey());
//                }
//                else if(o2.getValue() > o1.getValue())
//                {//                    return 1;
//                }
//                else {//                    return -1;
//                }}});for (Map.Entry<String, Double> e : list) {//System.out.println(e.getValue());System.out.println(e.getKey());}}public static class Person {String name;double cnt = 0.000;//double sum = 0;public Person(String s) {name = s;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Person person = (Person) o;return Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name);}}
}

关键点与问题

  1. 一人多次去一个地方,不计入分母次数,说明地点要与人进行一下绑定。
  2. 根据地点的得分为主关键字,地点字典顺序为次要关键字去排序。
  3. 人对应地点,地点对应得分,而地点的得分又依托于人物的轨迹,最后的排序又要依据地点的得分以及地点的字典顺序。
  4. 排序中会遇到问题,怎样将地点的得分与地点名字整合一起进行一个排序判断。并且在比较器重写过程中,因为得分极大可能是一个小数,小数之间的相减再强转int会导致精度丢失,误差很大。

代码思路

public static class Person {String name;double cnt = 0.000;//double sum = 0;public Person(String s) {name = s;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null || getClass() != o.getClass()) {return false;}Person person = (Person) o;return Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name);}}

首先创建一个人物类,包含string的name属性和double的cnt属性。分别代表人的名字和所到过的地方总数。(cnt的改变用逻辑实现所去地点不重复。)其中建立构造器收录name,重写equals()与hashCode()使其Person类Name相同代表为相同对象。此处实现后续的人物存储时候避免重复存储同一人物。

//人和地址集合
HashMap<Person, HashSet> map = new HashMap();
//地址集合
HashMap<String, Double> admap = new HashMap();

创建两个map,分别为map:人物(Person)-地点集合(HashSet);admap:地址(String)-地址得分(Double)

//数据预处理for (int i = 0; i < n; i++) {if(i==0){sc.nextLine();}String s = sc.nextLine();String[] ss = s.split(" ");admap.put(ss[1], 0.000);//System.out.println(admap.get(ss[1]));Person p = new Person(ss[0]);if (map.get(p) == null) {map.put(p, new HashSet());}//若加入else会导致第一个消失。map.get(p).add(ss[1]);}

数据预处理: 每行输入都输入一行字符串,包含名字和地点。在输入n之后会有一个回车,用一个nextLine()吸收回车。
用split()进行分割字符串,在此行字符串中,ss[0]代表名字,ss[1]代表地址。此处将地址集合admap进行初始化。
此后利用ss[0]实例化Person对象P,若map中无法找到此时P对象所对应的Value,则用put将new匿名类HashSet添加到map中去(此时添加的HashSet为空)。后续再利用map.get§.add(ss[1]);将此时的地址添加到此时map中P所对应的Value(即此人物对应的地址集合)中。
为方便解读,下文的人物P代表遍历状态下此时的Person对象,map的Value都代表此时人物P对应的地址集合。
数据预处理后达到一个状态:

  1. map中存储人物-地址集合,所输入的所有人都会进行相应处理,将此人物去过的所有地点添加到此人物对应的地址集合(P所对应的HashSet中)中去。
  2. admap中则存储了所有地址,但此时所有地址所对应得分都是0.0
Set<Person> set = map.keySet();for (Person p :set) {p.cnt = map.get(p).size();for (String ad :map.get(p)) {if (admap.containsKey(ad)) {admap.put(ad, admap.get(ad) + (1.0 / p.cnt));
//                    System.out.println(1/p.cnt);
//                    System.out.println(admap.get(ad));}}}

此后我们便可以通过遍历,逐个将map中的Value的size()赋值给P.cnt,再进行逐个运算,如果admap中的K(地址)包括此时P的地址ad,那么将此时的admap中ad所对应的值加上(1.0 / p.cnt)。

拓展: 上文的Map遍历方式有六种方式

  • 取出所有Key //常用方法

    1. 增强for循环
    2. 迭代器遍历

上述两种方法直接使用map中的Keyset取出所有Key的集合,然后依次使用get方法获取Value进行遍历输出

  • 取出Value遍历 //Values方法取出所有Value到一个Collection中

    1. 增强for循环
    2. 迭代器

上述两种直接使用Value变量输出就好

  • 通过EntrySet获取k-v来遍历输出

    1. 增强for循环
    2. 迭代器

上述两种使用EntrySet取出Entry的集合,使用getKey()与getValue()方法遍历即可

下文中的一个排序的实现就是取出Entry集合的方法。

如此遍历,最后进行一个排序,此排序有些复杂,但写的时候思路较为清晰。一班的TreeMap排序默认按照Key来排序,即使匿名内部类传入重写比较器也无法利用Value来进行比较。所以要么收录出所有的Key来进行一个算法的排序和存储,要么就把所有的Key或者Value取出放在一个集合中进行工具排序。

List<Map.Entry<String, Double>> list = new ArrayList<Map.Entry<String, Double>>(admap.entrySet());Collections.sort(list, new Comparator<Map.Entry<String, Double>>() {@Overridepublic int compare(Map.Entry<String, Double> o1, Map.Entry<String, Double> o2) {int t =  (int)((o2.getValue())*1000 - (o1.getValue()*1000));if(t==0){return o1.getKey().compareTo(o2.getKey());}return t;
//                if (o2.getValue().equals(o1.getValue()) ) {//                    return o1.getKey().compareTo(o2.getKey());
//                }
//                else if(o2.getValue() > o1.getValue())
//                {//                    return 1;
//                }
//                else {//                    return -1;
//                }}});

取出Map中封装的Entry对象,此对象就是将K-V变成了一个单列集合,因此可以直接给到ArrayList中。而后使用集合工具类的排序方法sort()进行排序,记得添加比较器。
比较器重写中的问题
注意如果遇到在比较器中重写,想利用相减来进行排序比较。这个比较器常常是返回int类型的,如果要比较 的关键词是double类型,直接用来相减后强转为int会损失精度,导致误差。如果是1以内就会自动转为0,这就代表两者相同,但事实并非如此。
在此题中,要么使用比较运算符进行判断返回,要么就找到此小数的最大有效位数(大概),将两个值都乘上相应的10的次方进行一个扩大,这样之后的强转整型就能最大限度不会破坏精度。
下方的向上取整方法是不可行的,因为若是单纯相减,会出现负小数,负数的向上取整和正数的向上取整是不同的。
除非利用判断语句,向上取整和向下取整都使用。四舍五入取整是不能用在这里的,当然你要是硬刚使用if语句,咱就是说,小刀划屁股了。

向上取整:Math.ceil(double a)
向下取整:Math.floor(double a)
四舍五入取整:Math.round(double a) 这里要注意Math.round(double a)和Math.rint(double a),四舍五入取整要用Math.round(double a),千万不要用Math.rint(double a)。
普通强转(int)xx【double】 会直接舍弃小数,损失精度。

感染源在哪里?-java题解相关推荐

  1. p3371 单源最短路径(弱化版)-java题解-最短路

    弱化版传送门: P3371 [模板]单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 本题测试数据为随机数据,在考试中可能会出现构造数据让SPFA不通 ...

  2. Redis源码和java jdk源码中hashcode的不同实现

    一.redis实际上是使用了siphash 这个比较简单,我说的简单是指redis代码比较少不像jdk一样调用C++代码调用栈非常深. 先看这个rehashing.c 主要就是dictKeyHash函 ...

  3. 面试有没有看过spring源码_如何看Spring源码、Java每日六道面试分享,打卡第二天...

    原标题:如何看Spring源码.Java每日六道面试分享,打卡第二天 想要深入的熟悉了解Spring源码,我觉得第一步就是要有一个能跑起来的极尽简单的框架,下面我就教大家搭建一个最简单的Spring框 ...

  4. 单例设计模式详解+源代码+JDK源码应用——Java设计模式系列学习笔记

    文章目录 一. 基本介绍 二. 单例模式的八种方式 1. 饿汉式(静态常量) 2. 饿汉式(静态代码块) 3. 懒汉式(线程不安全) 4. 懒汉式(线程安全,同步方法) 5. 懒汉式(线程安全,同步代 ...

  5. 深入源码分析Java线程池的实现原理

    转载自   深入源码分析Java线程池的实现原理 程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对 ...

  6. java list e 查找_源码(04) -- java.util.ListE

    java.util.List 源码分析(JDK1.7) ------------------------------------------------------------------------ ...

  7. java变量中不属于复合类型的数据类型是_2006新版JAVA题解(JAVA简单数据类型)...

    2006新版JAVA题解(JAVA简单数据类型) 2007年1月22日来源:233网校网校课程 在线题库评论 分享到 1.下列哪一个是合法的标识符: A.12class; B.+viod; C.-5; ...

  8. 深入解析棋牌湖南放炮罚,跑胡子手游源码(java版)

    深入解析棋牌湖南放炮罚,跑胡子手游后台源码(java版) 最近开发了一款湖南放炮罚的房卡模式带三级分销的手游,现在我就将我开发中的思路给朋友们分享一下. 首先介绍一下棋牌游戏最近的火热度吧. 最近微信 ...

  9. 2020第五届上海第二工业大学新生程序设计竞赛(Java题解)

    2020第五届上海第二工业大学新生程序设计竞赛(Java题解) 作为C/C++版本的补充题解,仅供参考 需要解析的可以看这篇: 2020第五届上海第二工业大学新生程序设计竞赛 用Java语言参加竞赛时 ...

最新文章

  1. gitlab解决一些问题
  2. python与excel做数据可视化-python数据可视化怎么做?excel可视化图表制作?
  3. spring boot同时启动多个服务副本(同一服务启动在不同端口)配置方法
  4. html表格以pdf格式导出到本地
  5. fastapi vue socket 从其他文件调用 socket 方法
  6. app inventor HTML5,[App Inventor] Web客戶端元件 POST 傳值的使用方式
  7. 面试官又整新活,居然问我for循环用i++和++i哪个效率高?
  8. Linux时间不准确的问题![转]
  9. InstallShield Build错误:Internal build error 6041
  10. windows :32位到64 位
  11. linux命令查看log.gz,linux命令学习系列13-gzip,tar命令
  12. 双轮载人平衡车设计完整教程之硬件篇
  13. Java后台调用高德地图api返回{status:0,info:UNKNOWN_ERROR,infocode:20003}
  14. 条形码转化成二维码_在线条形码生成器
  15. Netty In Action中文版 - 第一章:Netty介绍
  16. 在fpga中用Cordic算法来产生正弦函数
  17. 使用go获取LDAP的baseDN以及验证登录
  18. ccf-csp 2013-2015题目总结
  19. 手把手教你从0开始搭建个人博客,东半球最详细的保姆级博客搭建部署教程 | 程序员人手必备个人博客网站
  20. 综合素质——作文——规则意识、人品教育、自信、用于探索、开拓前行;

热门文章

  1. linux 在线对比工具,值得收藏!6款超棒的在线工具合集
  2. 微信小程序播放背景音乐及开发工具没有声音问题解决
  3. 原生 CSS Custom Highlight 终于来了
  4. 华秋HDI板脱颖而出的密码
  5. 新萝卜家园GHOST WIN7系统32,64位极速装机特别版
  6. PPT2010自定义动作路径!(很多人都找不到,图解!)
  7. 【qq机器人】检测群员退群通报
  8. Putty使用秘钥认证时,报错:Disconnected: No supported authentication methods available
  9. Thinkpad X1 yoga 启动时卡在 Flashing Embedded Controller
  10. swf 在html页面平铺,四种方法实现动画SWF文件全屏效果