问题描述
解题思路
数学基础
需要一些很简单的数论基础知识(主要是同余
的知识),参考数论基础(oi-wiki)。
模拟思路
将整个计算登机牌条码的完整过程拆分成以下步骤:
对字符进行编码
:PDF417::PDF417Encoder::Encoder::EncodeCharacters()
对编码后的值进行填充
:PDF417::PDF417Encoder::Encoder::PadEncodedValue()
将编码并填充后的值转换成码字
:PDF417::PDF417Encoder::Encoder::CalcCodeWord()
对码字进行填充
:PDF417::PDF417Encoder::Encoder::PadCodeWord()
计算校验码
:PDF417::PDF417Encoder::Encoder::CalcCheckSum()
这一步骤涉及一些简单的多项式乘法、多项式除法以及同余的知识。
打印结果
:PDF417::PDF417Encoder::Encoder::Print()
得分技巧
计算校验和
的过程相对来其他过程来说难
了一点。在无法完成这一步骤的情况下,只完成其他步骤也能轻松拿40分。
AC代码
后面几条提交
PA
是因为没有及时取模
,导致整数溢出了。
关于编码风格的说明:习惯性地使用了一些面向对象的思想对代码进行封装。实际考试时考虑到编码效率,可能没必要这么做。
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <cmath>
namespace PDF417
{
class PDF417Encoder
{
private:
class Encoder
{
private:
enum class EncoderStatus
{
Uppercase, Lowercase, Number
};
using EncodedValueType = char;
using CodeWordType = short;
public:
Encoder(size_t LineCapacity, int ChecksumLevel, std::string DataToBeEncoded)
: m_EncoderStatus(EncoderStatus::Uppercase),
m_LineCapacity(LineCapacity), m_ChecksumNum(ChecksumLevel == -1 ? 0 : (1 << (ChecksumLevel + 1))), m_DataToBeEncoded(DataToBeEncoded)
{ }
public:
void Encode()
{
EncodeCharacters();
PadEncodedValue();
CalcCodeWord();
PadCodeWord();
CalcCheckSum();
Print();
}
private :
inline std::pair<EncoderStatus, EncodedValueType> EncodeCharacter(const char &Character)
{
if(Character >= 'a' && Character <= 'z') return std::make_pair<EncoderStatus, EncodedValueType>(EncoderStatus::Lowercase, Character - 'a');
if(Character >= 'A' && Character <= 'Z') return std::make_pair<EncoderStatus, EncodedValueType>(EncoderStatus::Uppercase, Character - 'A');
// if(Character >= '0' && Character <= '9')
return std::make_pair<EncoderStatus, EncodedValueType>(EncoderStatus::Number, Character - '0');
}
void TransformEncoderStatusTo(const EncoderStatus &EncoderStatusTo)
{
if(m_EncoderStatus == EncoderStatusTo) return;
do
{
// Transform To Lowercase
if(EncoderStatusTo == EncoderStatus::Lowercase)
{
m_EncodedValueVector.emplace_back(27);
break;
}
// Transform To Number
if(EncoderStatusTo == EncoderStatus::Number)
{
m_EncodedValueVector.emplace_back(28);
break;
}
// Transform To Uppercase
if(EncoderStatusTo == EncoderStatus::Uppercase)
{
// Transform Number To Uppercase
if(m_EncoderStatus == EncoderStatus::Number)
{
m_EncodedValueVector.emplace_back(28);
break;
}
// Transform Lowercase To Uppercase
m_EncodedValueVector.emplace_back(28);
m_EncodedValueVector.emplace_back(28);
break;
}
}
while(false);
m_EncoderStatus = EncoderStatusTo;
}
private:
void EncodeCharacters()
{
for(const char &Character : m_DataToBeEncoded)
{
std::pair<EncoderStatus, EncodedValueType> EncodedResult = EncodeCharacter(Character);
EncoderStatus EncoderStatusTo = EncodedResult.first;
TransformEncoderStatusTo(EncoderStatusTo);
m_EncodedValueVector.emplace_back(EncodedResult.second);
}
}
void PadEncodedValue()
{
int DataLength = m_EncodedValueVector.size();
if(DataLength & 1) m_EncodedValueVector.emplace_back(29);
}
void CalcCodeWord()
{
int EncodedValueNum = m_EncodedValueVector.size();
for(int i = 0; i < EncodedValueNum; i += 2)
{
m_CodeWordDeque.emplace_back(m_EncodedValueVector[i] * 30 + m_EncodedValueVector[i + 1]);
}
}
void PadCodeWord()
{
int DataCodeWordNum = m_CodeWordDeque.size();
int CodeWordNumRemainder = (m_ChecksumNum + DataCodeWordNum + 1) % m_LineCapacity;
int PaddingTimes = CodeWordNumRemainder ? m_LineCapacity - CodeWordNumRemainder : 0;
for(int i = 0; i < PaddingTimes; ++i)
{
m_CodeWordDeque.emplace_back(900);
}
m_CodeWordDeque.emplace_front(DataCodeWordNum + 1 + PaddingTimes);
}
void CalcCheckSum()
{
if(m_ChecksumNum == 0) return;
// g(x) = (x - 3)(x - 9)(x - 27)······
std::deque<int> Polynomial_gx({1, -3});
int TempNumber = -3;
for(int i = 1; i < m_ChecksumNum; ++i)
{
TempNumber *= 3;
TempNumber %= 929;
int n = Polynomial_gx.size();
Polynomial_gx.emplace_back(0);
for(int j = n; j >= 1; --j)
{
Polynomial_gx[j] += Polynomial_gx[j - 1] * TempNumber;
Polynomial_gx[j] %= 929;
}
}
// d(x) = d_(n-1) * x ^ (n - 1) + ·····
std::deque<int> Polynomial_dx;
for(const int &Coefficient : m_CodeWordDeque)
{
Polynomial_dx.emplace_back(Coefficient);
}
// f(x) = d(x) * x ^ k
std::deque<int> Polynomial_fx = std::move(Polynomial_dx);
for(int i = 0; i < m_ChecksumNum; ++i)
{
Polynomial_fx.emplace_back(0);
}
while(Polynomial_fx.size() >= Polynomial_gx.size())
{
int Quotient = Polynomial_fx.front();
for(int i = 0, n = Polynomial_gx.size(); i < n; ++i)
{
Polynomial_fx[i] -= Polynomial_gx[i] * Quotient;
Polynomial_fx[i] %= 929;
}
if(Polynomial_fx.front() == 0) Polynomial_fx.pop_front();
}
//
for(int &Coefficient : Polynomial_fx)
{
Coefficient = - Coefficient;
Coefficient %= 929;
if(Coefficient < 0) Coefficient += 929;
m_CodeWordDeque.emplace_back(Coefficient);
}
}
void Print()
{
for(const CodeWordType &CodeWord_i : m_CodeWordDeque)
{
std::cout << CodeWord_i << std::endl;
}
}
private:
EncoderStatus m_EncoderStatus;
std::vector<EncodedValueType> m_EncodedValueVector;
std::deque<CodeWordType> m_CodeWordDeque;
size_t m_LineCapacity;
int m_ChecksumNum;
std::string m_DataToBeEncoded;
};
public:
PDF417Encoder() = default;
void Encode(size_t LineCapacity, int ChecksumLevel, std::string DataToBeEncoded)
{
Encoder EncoderEntity(LineCapacity, ChecksumLevel, std::move(DataToBeEncoded));
EncoderEntity.Encode();
}
};
}
int main()
{
// IO initialization
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
// input line capacity and checksum level
size_t LineCapacity;
int ChecksumLevel;
std::cin >> LineCapacity >> ChecksumLevel;
// input data that needs to be encoded
std::string DataToBeEncoded;
std::cin >> DataToBeEncoded;
// Encode
PDF417::PDF417Encoder EncoderEntity;
EncoderEntity.Encode(LineCapacity, ChecksumLevel, std::move(DataToBeEncoded));
return 0;
}