一 问题描述
设有 n 家客户,n <= 24,每个客户有如下属性(客户名称、客户类型、经度、纬度、每月拜访次数)。
每个客户 m 天拜访 1 次。
每个客户每天最多拜访 8 次。
给出一个月的工作日列表。
求一个月拜访轨迹。
二 输入和输出
1 输入说明
第 1 行代表客户数 n 和拜访频率 m。
接下来的 n 行描述每个客户,每个客户信息包括(客户名称、客户类型、经度、纬度、每月拜访次数),客户类型有:M-商业拜访,H-医院拜访、P-药房拜访
接下来的一行是工作日列表
2 输出说明
药代这个月每天拜访轨迹。
三 输入和输出样例
1 输入样例
24 3
H1 H 117.196067 39.103303 8
H2 H 117.206175 39.111509 8
H3 H 117.187551 39.109744 8
H4 H 117.179981 39.113295 8
H5 H 117.183536 39.125713 8
H6 H 117.209626 39.12228 8
H7 H 117.233087 39.082267 8
H8 H 117.195568 39.111846 8
H9 H 117.207421 39.121483 8
H10 H 117.191717 39.104914 8
H11 H 117.200753 39.109458 8
H12 H 117.182597 39.102957 8
H13 H 117.188228 39.128191 8
H14 H 117.193003 39.121008 8
H15 H 117.190858 39.135014 8
H16 H 117.179979 39.11038 8
M1 B 117.18461 39.117381 4
M2 B 117.206176 39.127932 4
M3 B 117.185073 39.124452 4
M4 B 117.183584 39.121472 4
M5 B 117.172522 39.126702 4
M6 B 117.185248 39.104909 4
M7 B 117.192618 39.129049 4
M8 B 117.181509 39.123574 4
1 2 3 4 5 7 8 9 10 11 12 14 15 16 17 18 19 21 22 23 24 25 26 28 29 30
2 输出样例
1 号拜访客户:H9 > H6 > M2 > H15 > M8 > M5 > H3 > H12 >
2 号拜访客户:H4 > H16 > M6 > H10 > H1 > M3 > H13 > H7 >
3 号拜访客户:M7 > H5 > M4 > M1 > H14 > H8 > H11 > H2 >
4 号拜访客户:M2 > H6 > H9 > H15 > M8 > M5 > H3 > H12 >
5 号拜访客户:H1 > H10 > M6 > H16 > H4 > M3 > H13 > H7 >
7 号拜访客户:H14 > M4 > M1 > H5 > M7 > H8 > H11 > H2 >
8 号拜访客户:H15 > M8 > M5 > H3 > H12 > H9 > H6 > M2 >
9 号拜访客户:H7 > H1 > H10 > M6 > H16 > H4 > M3 > H13 >
10 号拜访客户:H5 > M4 > M1 > H14 > M7 > H8 > H11 > H2 >
11 号拜访客户:H12 > H3 > M8 > M5 > H15 > M2 > H6 > H9 >
12 号拜访客户:H7 > H1 > H10 > M6 > H16 > H4 > M3 > H13 >
14 号拜访客户:H8 > H11 > H2 > H14 > M4 > M1 > H5 > M7 >
15 号拜访客户:H6 > H9 > H15 > H3 > H12 >
16 号拜访客户:H7 > H1 > H10 > H16 > H4 > H13 >
17 号拜访客户:H8 > H11 > H2 > H14 > H5 >
18 号拜访客户:H3 > H12 > H9 > H6 > H15 >
19 号拜访客户:H16 > H4 > H10 > H1 > H13 > H7 >
21 号拜访客户:H8 > H11 > H2 > H14 > H5 >
22 号拜访客户:H9 > H6 > H15 > H3 > H12 >
23 号拜访客户:H13 > H4 > H16 > H10 > H1 > H7 >
24 号拜访客户:H5 > H14 > H8 > H11 > H2 >
25 号拜访客户:H3 > H12 > H9 > H6 > H15 >
26 号拜访客户:H4 > H16 > H10 > H1 > H13 > H7 >
28 号拜访客户:H8 > H11 > H2 > H14 > H5 >
四 代码
package com.platform.modules.alg.alglib.sdgt0002;
import java.util.*;
public class Sdgt0002 {
public String output = "";
/**
* 默认地球半径
*/
private static double EARTH_RADIUS = 6371000; // 赤道半径(单位m)
private final int inf = 0x3f3f3f3f;
// 客户数
private int n;
// 拜访频率,几天拜访一次
private int frequency;
// 拜访天数,每月拜访多少天
private int days;
// 客户批次 map,第1个泛型代表第几批客户,第2个泛型代表批次客户列表
private Map<Integer, List<Customer>> batchCustomerMap = new HashMap<>();
// 拜访轨迹 map,第1个泛型代表每月第几天,第2个泛型代表该天拜访的客户列表
private Map<Integer, List<Customer>> visitMap = new HashMap<>();
// 轨迹计算
public String cal(String input) {
String[] line = input.split("\n");
String[] params = line[0].split(" ");
n = Integer.parseInt(params[0]); // 客户数
frequency = Integer.parseInt(params[1]); // 几天拜访一次
//days = Integer.parseInt(params[2]); // 每月拜访多少天
// 对输入数据进行处理
for (int i = 1; i <= n; i++) {
String[] customerStr = line[i].split(" ");
Customer customer = new Customer();
customer.setName(customerStr[0]); // 客户名称
customer.setType(customerStr[1]); // 客户类别
customer.setLocationLon(Double.parseDouble(customerStr[2])); // 经度
customer.setLocationLat(Double.parseDouble(customerStr[3])); // 纬度
customer.setTotalCount(Integer.parseInt(customerStr[4])); // 每月最多拜访次数
// 批次客户列表加第1个客户,如果 frequency = 3,则批次下标范围 [0,1,2]
if (batchCustomerMap.get(i % frequency) == null) {
List<Customer> batchCustomerList = new ArrayList();
batchCustomerList.add(customer);
batchCustomerMap.put(i % frequency, batchCustomerList);
} else { // 批次客户列表加其他客户
batchCustomerMap.get((i % frequency)).add(customer);
}
}
// 模拟每月的几号
String[] days = line[n + 1].split(" ");
List batchDays[] = new ArrayList[frequency];
for (int i = 0; i < batchDays.length; i++) {
batchDays[i] = new ArrayList();
}
for (int i = 0; i < days.length; i++) {
int batchNum = i % frequency;
batchDays[batchNum].add(days[i]);
}
// 每日拜访处理
for (int i = 0; i < days.length; i++) {
int batch = 0;
for (List batchDay : batchDays) {
if (batchDay.contains(days[i])) {
batch = i % frequency;
break;
}
}
// 获取某批次客户列表
List<Customer> customers = batchCustomerMap.get(batch);
// 打乱批次客户列表顺序
Collections.shuffle(customers);
List<Customer> selectCustomers = new ArrayList<>();
// 选择前8个客户
for (int j = 0; j < customers.size(); j++) {
if (customers.get(j).getTotalCount() > 0) {
selectCustomers.add(customers.get(j));
if (selectCustomers.size() >= 8) {
break;
}
}
}
// 每日拜访的第1个客户
if (selectCustomers.size() == 0) continue;
Customer startCustomer = selectCustomers.get(0);
// 标识该客户已访问
startCustomer.setVisited(true);
// 修正剩余可拜访次数
int plusCount = startCustomer.getTotalCount() - 1;
startCustomer.setTotalCount(plusCount);
// 创建第 i 号的拜访客户列表
List<Customer> oneDayCustomerList = new ArrayList();
visitMap.put(i, oneDayCustomerList);
// 向 i 号拜访客户列表加第1个客户
oneDayCustomerList.add(startCustomer);
Customer firstCustomer; // 两次相邻拜访的前一个客户
Customer secondCustomer; // 两次相邻拜访的后一个客户
Customer nextSelectCustomer = new Customer(); // 下一个选中的客户
double tempMinDistance; // 临时最短距离
firstCustomer = startCustomer;
for (int m = 1; m < selectCustomers.size(); m++) { // 从第2个客户开始处理
double minDistance = inf; // 初始化最短距离
for (int n = 1; n < selectCustomers.size(); n++) { // 从第2个客户开始处理
secondCustomer = selectCustomers.get(n);
// 两次相邻拜访的后一个客户如果已经拜访或剩余可拜访次数为0,不处理
if (secondCustomer.isVisited() || secondCustomer.getTotalCount() == 0) {
continue;
}
// 计算相邻两个客户的距离
tempMinDistance = GetDistance(firstCustomer.getLocationLon(), firstCustomer.getLocationLat(), secondCustomer.getLocationLon(), secondCustomer.getLocationLat());
if (tempMinDistance < minDistance) {
// 更新最短距离
minDistance = tempMinDistance;
// 最短距离对应的客户
nextSelectCustomer = secondCustomer;
}
}
// 下一个被选中的客户设置为已拜访
nextSelectCustomer.setVisited(true);
int plusTimes = nextSelectCustomer.getTotalCount() - 1;
// 下一个被选中的客户剩余拜访次数
nextSelectCustomer.setTotalCount(plusTimes);
// 向 i 号拜访客户列表加选中的客户
visitMap.get(i).add(nextSelectCustomer);
firstCustomer = nextSelectCustomer;
}
// visited 只对当天拜访起作用,所以要清空 visited 标识
for (Customer selectCustomer : customers) {
selectCustomer.setVisited(false);
}
}
// 输出处理
for (int i = 0; i < days.length; i++) {
List<Customer> customers = visitMap.get(i);
if (customers == null) {
continue;
}
output += days[i] + " 号拜访客户:";
for (Customer customer : customers) {
output += customer.getName() + " > ";
}
output += "\n";
}
return output;
}
/**
* 功能描述:通过经纬度计算两点之间的距离
*
* @param lon1 第 1 个点的经度
* @param lat1 第 1 个点的纬度
* @param lon2 第 2 个点经度
* @param lat2 第 2 个点纬度
* @return 两点间的距离
* @author chengqiuming
* @date 2022/10/25
* @description:
*/
public double GetDistance(double lon1, double lat1, double lon2, double lat2) {
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lon1) - rad(lon2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
return s;
}
/**
* 转化为弧度(rad)
*/
private static double rad(double d) {
return d * Math.PI / 180.0;
}
}
class Customer {
// 客户名称
private String name;
// 客户类型 H-医院客户 M-商业公司客户 P-药房拜访
private String type;
// 经度
private Double locationLon;
// 纬度
private Double locationLat;
// 每月最多可拜访次数
private int totalCount;
// 是否已拜访
private boolean visited = false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Double getLocationLon() {
return locationLon;
}
public void setLocationLon(Double locationLon) {
this.locationLon = locationLon;
}
public Double getLocationLat() {
return locationLat;
}
public void setLocationLat(Double locationLat) {
this.locationLat = locationLat;
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
}
五 测试
1 输入
24 3
H1 H 117.196067 39.103303 8
H2 H 117.206175 39.111509 8
H3 H 117.187551 39.109744 8
H4 H 117.179981 39.113295 8
H5 H 117.183536 39.125713 8
H6 H 117.209626 39.12228 8
H7 H 117.233087 39.082267 8
H8 H 117.195568 39.111846 8
H9 H 117.207421 39.121483 8
H10 H 117.191717 39.104914 8
H11 H 117.200753 39.109458 8
H12 H 117.182597 39.102957 8
H13 H 117.188228 39.128191 8
H14 H 117.193003 39.121008 8
H15 H 117.190858 39.135014 8
H16 H 117.179979 39.11038 8
M1 B 117.18461 39.117381 4
M2 B 117.206176 39.127932 4
M3 B 117.185073 39.124452 4
M4 B 117.183584 39.121472 4
M5 B 117.172522 39.126702 4
M6 B 117.185248 39.104909 4
M7 B 117.192618 39.129049 4
M8 B 117.181509 39.123574 4
1 2 3 4 5 7 8 9 10 11 12 14 15 16 17 18 19 21 22 23 24 25 26 28 29 30
2 输出
1 号拜访客户:H15 > M8 > M5 > H3 > H12 > H9 > H6 > M2 >
2 号拜访客户:H1 > H10 > M6 > H16 > H4 > M3 > H13 > H7 >
3 号拜访客户:H14 > M4 > M1 > H5 > M7 > H8 > H11 > H2 >
4 号拜访客户:M2 > H6 > H9 > H15 > M8 > M5 > H3 > H12 >
5 号拜访客户:H13 > M3 > H4 > H16 > M6 > H10 > H1 > H7 >
7 号拜访客户:H14 > M4 > M1 > H5 > M7 > H8 > H11 > H2 >
8 号拜访客户:H6 > H9 > M2 > H15 > M8 > M5 > H3 > H12 >
9 号拜访客户:M3 > H13 > H4 > H16 > M6 > H10 > H1 > H7 >
10 号拜访客户:M1 > M4 > H5 > M7 > H14 > H8 > H11 > H2 >
11 号拜访客户:M8 > M5 > H15 > M2 > H6 > H9 > H3 > H12 >
12 号拜访客户:M3 > H13 > H4 > H16 > M6 > H10 > H1 > H7 >
14 号拜访客户:H8 > H11 > H2 > H14 > M4 > M1 > H5 > M7 >
15 号拜访客户:H15 > H9 > H6 > H3 > H12 >
16 号拜访客户:H7 > H1 > H10 > H16 > H4 > H13 >
17 号拜访客户:H5 > H14 > H8 > H11 > H2 >
18 号拜访客户:H12 > H3 > H9 > H6 > H15 >
19 号拜访客户:H1 > H10 > H16 > H4 > H13 > H7 >
21 号拜访客户:H11 > H2 > H8 > H14 > H5 >
22 号拜访客户:H9 > H6 > H15 > H3 > H12 >
23 号拜访客户:H4 > H16 > H10 > H1 > H13 > H7 >
24 号拜访客户:H2 > H11 > H8 > H14 > H5 >
25 号拜访客户:H12 > H3 > H9 > H6 > H15 >
26 号拜访客户:H1 > H10 > H16 > H4 > H13 > H7 >
28 号拜访客户:H11 > H2 > H8 > H14 > H5 >