mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-18 02:10:21 +08:00
feat: 初步跑通抄作业功能,并应用分组算法
This commit is contained in:
250
src/MaaCore/Utils/Algorithm.hpp
Normal file
250
src/MaaCore/Utils/Algorithm.hpp
Normal file
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace asst::algorithm
|
||||
{
|
||||
std::optional<std::unordered_map<std::string, std::string>> get_char_allocation_for_each_group(
|
||||
const std::unordered_map<std::string, std::vector<std::string>>& group_list,
|
||||
const std::unordered_set<std::string>& char_set)
|
||||
{
|
||||
/*
|
||||
* * dlx 算法简介
|
||||
*
|
||||
* https://oi-wiki.org/search/dlx/
|
||||
*
|
||||
*
|
||||
* * dlx 算法作用
|
||||
*
|
||||
* 在形如:
|
||||
* a: 10010
|
||||
* b: 01110
|
||||
* c: 01001
|
||||
* d: 00100
|
||||
* e: 11010
|
||||
* 这样的数据里,
|
||||
* dlx 可以找到 {a, c, d} 这样每列恰好出现且仅出现一次 1 的数据,
|
||||
* 也即对全集的一个精确覆盖:
|
||||
* a: 10010
|
||||
* c: 01001
|
||||
* d: 00100
|
||||
* 11111
|
||||
*
|
||||
*
|
||||
* * dlx 算法建模
|
||||
*
|
||||
* dlx 的列分为 [组号] [干员号] 两部分
|
||||
* dlx 的行分为 [可能的选择对] [不选择该干员] 两部分
|
||||
*
|
||||
* [可能的选择对]:
|
||||
* 每行对应一种可能的选择,
|
||||
* 将组号,干员号对应位置的列设为1
|
||||
*
|
||||
* [不选择该干员]:
|
||||
* 每行对应不选择某干员的情况,
|
||||
* 将干员号对应位置的列设为1
|
||||
*
|
||||
*
|
||||
* * dlx 建模示例
|
||||
*
|
||||
* 有以下分组:
|
||||
* a: {1, 3, 4}
|
||||
* b: {2, 3, 5}
|
||||
* c: {1, 2, 3}
|
||||
* 拥有的干员:
|
||||
* {1, 2, 4, 5, 6}
|
||||
*
|
||||
* 先处理出所有可能的情况:
|
||||
* a: {1, 4}
|
||||
* b: {2, 5}
|
||||
* c: {1, 2}
|
||||
*
|
||||
* 构造表:
|
||||
* abc 1245
|
||||
* 1 100 1000 <a, 1>
|
||||
* 2 100 0010 <a, 4>
|
||||
* 3 010 0100 <b, 2>
|
||||
* 4 010 0001 <b, 5>
|
||||
* 5 001 1000 <c, 1>
|
||||
* 6 001 0100 <c, 2>
|
||||
* 7 000 1000 ~1
|
||||
* 9 000 0100 ~2
|
||||
* 9 000 0010 ~4
|
||||
* A 000 0001 ~5
|
||||
*
|
||||
* 使用dlx求得一组解:
|
||||
* 一个可能的结果是:
|
||||
* 行号 {2, 3, 5, A}
|
||||
* 即 {<a, 4>, <b, 2>, <c, 1>, ~5}
|
||||
*
|
||||
* 输出分组结果:
|
||||
* a: 4
|
||||
* b: 2
|
||||
* c: 1
|
||||
*
|
||||
*/
|
||||
|
||||
// dlx 算法模板类
|
||||
class DancingLinksModel
|
||||
{
|
||||
private:
|
||||
size_t index {};
|
||||
std::vector<size_t> first, size;
|
||||
std::vector<size_t> left, right, up, down;
|
||||
std::vector<size_t> column, row;
|
||||
|
||||
void remove(const size_t& column_id)
|
||||
{
|
||||
left[right[column_id]] = left[column_id];
|
||||
right[left[column_id]] = right[column_id];
|
||||
for (size_t i = down[column_id]; i != column_id; i = down[i]) {
|
||||
for (size_t j = right[i]; j != i; j = right[j]) {
|
||||
up[down[j]] = up[j];
|
||||
down[up[j]] = down[j];
|
||||
--size[column[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recover(const size_t& column_id)
|
||||
{
|
||||
for (size_t i = up[column_id]; i != column_id; i = up[i]) {
|
||||
for (size_t j = left[i]; j != i; j = left[j]) {
|
||||
up[down[j]] = down[up[j]] = j;
|
||||
++size[column[j]];
|
||||
}
|
||||
}
|
||||
left[right[column_id]] = right[left[column_id]] = column_id;
|
||||
}
|
||||
|
||||
public:
|
||||
size_t answer_stack_size {};
|
||||
std::vector<size_t> answer_stack;
|
||||
|
||||
DancingLinksModel(const size_t& max_node_num, const size_t& max_ans_size)
|
||||
: first(max_node_num), size(max_node_num), left(max_node_num), right(max_node_num), up(max_node_num),
|
||||
down(max_node_num), column(max_node_num), row(max_node_num), answer_stack(max_ans_size)
|
||||
{}
|
||||
|
||||
void build(const size_t& column_id)
|
||||
{
|
||||
for (size_t i = 0; i <= column_id; i++) {
|
||||
left[i] = i - 1;
|
||||
right[i] = i + 1;
|
||||
up[i] = down[i] = i;
|
||||
}
|
||||
left[0] = column_id;
|
||||
right[column_id] = 0;
|
||||
index = column_id;
|
||||
first.clear();
|
||||
size.clear();
|
||||
}
|
||||
|
||||
void insert(const size_t& row_id, const size_t& column_id)
|
||||
{
|
||||
column[++index] = column_id;
|
||||
row[index] = row_id;
|
||||
++size[column_id];
|
||||
down[index] = down[column_id];
|
||||
up[down[column_id]] = index;
|
||||
up[index] = column_id;
|
||||
down[column_id] = index;
|
||||
if (!first[row_id]) {
|
||||
first[row_id] = left[index] = right[index] = index;
|
||||
}
|
||||
else {
|
||||
right[index] = right[first[row_id]];
|
||||
left[right[first[row_id]]] = index;
|
||||
left[index] = first[row_id];
|
||||
right[first[row_id]] = index;
|
||||
}
|
||||
}
|
||||
|
||||
bool dance(const size_t& depth)
|
||||
{
|
||||
if (!right[0]) {
|
||||
answer_stack_size = depth;
|
||||
return true;
|
||||
}
|
||||
size_t column_id = right[0];
|
||||
for (size_t i = right[0]; i != 0; i = right[i]) {
|
||||
if (size[i] < size[column_id]) {
|
||||
column_id = i;
|
||||
}
|
||||
}
|
||||
remove(column_id);
|
||||
for (size_t i = down[column_id]; i != column_id; i = down[i]) {
|
||||
answer_stack[depth] = row[i];
|
||||
for (size_t j = right[i]; j != i; j = right[j]) {
|
||||
remove(column[j]);
|
||||
}
|
||||
if (dance(depth + 1)) {
|
||||
return true;
|
||||
}
|
||||
for (size_t j = left[i]; j != i; j = left[j]) {
|
||||
recover(column[j]);
|
||||
}
|
||||
}
|
||||
recover(column_id);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 建立结点、组、干员与各自 id 的映射关系
|
||||
std::vector<std::pair<std::string, std::string>> node_id_mapping;
|
||||
std::vector<std::string> group_id_mapping;
|
||||
std::vector<std::string> char_id_mapping;
|
||||
std::unordered_map<std::string, size_t> group_name_mapping;
|
||||
std::unordered_map<std::string, size_t> char_name_mapping;
|
||||
|
||||
for (auto& i : group_list) {
|
||||
group_name_mapping[i.first] = group_id_mapping.size();
|
||||
group_id_mapping.emplace_back(i.first);
|
||||
for (auto& j : i.second) {
|
||||
if (char_set.contains(j)) {
|
||||
node_id_mapping.emplace_back(i.first, j);
|
||||
if (!char_name_mapping.contains(j)) {
|
||||
char_name_mapping[j] = char_id_mapping.size();
|
||||
char_id_mapping.emplace_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 建 01 矩阵
|
||||
const size_t node_num = node_id_mapping.size();
|
||||
const size_t group_num = group_id_mapping.size();
|
||||
const size_t char_num = char_id_mapping.size();
|
||||
|
||||
DancingLinksModel dancing_links_model(2 * node_num + group_num + 2 * char_num + 1, group_num + char_num);
|
||||
|
||||
dancing_links_model.build(group_num + char_num);
|
||||
|
||||
for (size_t i = 0; i < node_num; i++) {
|
||||
dancing_links_model.insert(i + 1, group_name_mapping[node_id_mapping[i].first] + 1);
|
||||
dancing_links_model.insert(i + 1, group_num + char_name_mapping[node_id_mapping[i].second] + 1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < char_num; i++) {
|
||||
dancing_links_model.insert(i + node_num + 1, i + group_num + 1);
|
||||
}
|
||||
|
||||
// dance!!
|
||||
bool has_solution = dancing_links_model.dance(0);
|
||||
|
||||
// 判定结果
|
||||
if (!has_solution) return std::nullopt;
|
||||
|
||||
std::unordered_map<std::string, std::string> return_value;
|
||||
|
||||
for (size_t i = 0; i < dancing_links_model.answer_stack_size; i++) {
|
||||
if (dancing_links_model.answer_stack[i] > node_num) break;
|
||||
return_value.insert(node_id_mapping[dancing_links_model.answer_stack[i] - 1]);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
} // namespace asst::algorithm
|
||||
Reference in New Issue
Block a user