原题
描述
一元多项式是有序线性表的典型应用,用一个长度为m且每个元素有两个数据项(系数项和指数项)的线性表((p1,e1),(p2,e2),...,(pm,em))可以唯一地表示一个多项式。 本题实现多项式的相加和相乘运算。本题输入不保证有序。
输入
第1行:
一个整数m,表示第一个一元多项式的长度
第2行:
2m项,p1 e1 p2 e2 ...,中间以空格分割,表示第1个多项式系数和指数
第3行:
一个整数n,表示第二个一元多项式的项数
第4行:
有2n项,p1 e1 p2 e2 ...,中间以空格分割,表示第2个多项式系数和指数
第5行:
1个整数,若为0,执行加法运算并输出结果,若为1,执行乘法运算并输出结果;若为2,输出一行加法结果和一行乘法的结果。
输出
运算后的多项式链表,要求按指数从小到大排列
当运算结果为0 0时,不输出。
样例输入
4
7 0 3 1 9 8 5 17
3
8 1 22 7 -9 8
0
样例输出
7 0 11 1 22 7 5 17
解题
分析
既然题目要求使用线性表完成本题,那我们就要用线性表做题(而不是链表)。
多项式的每个项,包含指数和底数部分。所以,线性表的每个节点应该涵盖这两个内容。
按照比较好的设计,每个节点内容应如下:
struct Node {
int expo; // 指数
int base; // 底数
};
但是,如果真的按照这种方式设计,在进行乘法计算时会涉及一些困难。有兴趣的读者可以自行尝试。考虑到笔者水平受限,采用另一种相对简单的方式。
考虑到底数千变万化,且在不同项中可以重复,而每个项的指数都是独一无二的,我们可以创建一个数组,以指数为下标,每元素存储指数对应的底数。
如:a[5] = 4
表示 45 这项。对于底数为0的项,依旧存在数组里,只是值为0而已。这样做会额外消耗内存,但对如笔者一样的萌新很友好。
如此,每个多项式中只需要存储一个数组,并记录数组长度即可。
为什么要记录数组长度?因为我们不知道指数最大为多少,所以涉及动态内存申请内容。
对于一个完整的多项式,散装似乎不太好。我们采用面向对象的思想,将多项式的内容及功能(乘法、加法、打印等)打包成一个类,后续对整体对象进行操作,会方便很多。
设计
多项式类基本成员设计
设计多项式类如下(虽说是类,此处实际采用结构体存储。区别不大,但在工程中似乎还是用类更好):
struct Polynomial {
long long* baseVal = NULL;
size_t arrMax = -1;
void functions(...) {
...
}
};
Polynomial
意为多项式。
baseVal
即上述数组,负责存储底数。如果你不知道什么是 size_t,可以先用 int 代替。
arrMax
用于标记数组最后一个元素的位置。即:多项式中指数最大值。
初始状态,多项式没有内容,所以 baseVal 应指向一个空地址。多项式中没有元素,所以指数最大值比0还小。我们人为设定这种情况下最大指数为 -1.
功能函数设计
思考多项式类需要具有哪些能力?
首先,它需要具有被赋值的能力。不然,它不就永远是0多项式了么。
然后,需要有一个输出能力。似乎可以设计一个一项项读取的功能,然后从外部控制输出。但是我们完全可以为它设计一个能力,直接把自己完整输出,这样岂不是更方便?
按照题目要求,该多项式还应有加法和乘法能力。
由于类内涉及动态内存申请,所以需要设计一个清理功能,用于释放申请到的内存。(当然,你可以不做这一步,毕竟现在的操作系统会自动回收内存。但是,万一出现内存泄漏(如Android Lollipop),后果自负)
关于赋值,读者可能会问:谁给谁赋值?对此,我们会想到用一个多项式给另一个多项式赋值,即多项式拷贝。但是,第一个多项式怎么来(鸡生蛋,蛋生鸡,那么第一只鸡谁来生)?所以,我们可以加入一个用整数给多项式赋值的功能。
想法很好!但,如何用整数给多项式赋值?如果多项式有一百项,设计一个带二百个参数的函数?
聪明的你一定想到了传递数组!可以,不过此处采用另一种方式:
为多项式添加更新功能,传入一个底数和一个指数,将它添加到多项式中。
功能设计完毕,这就开写!
功能函数实现
更新
更新过程,传入参数为一个项的指数和底数。判断,如果baseVal
数组长度足以包含这个指数,就直接赋值。否则,要重新申请空间。
/**
* 项更新。
*
* @param base 底数。
* @param expo 指数。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void Polynomial::update(int base, int expo) {
// 判断是否需要重新申请内存
if (expo > arrMax) {
// 需要重新申请内存
long long* newArray;
newArray = (long long*) realloc(baseVal, (expo + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
} else {
// 申请成功
baseVal = newArray;
}
for (size_t i = arrMax + 1; i < expo; i++) {
baseVal[i] = 0; // 将新申请到的内容设为0.
}
arrMax = expo; // 记录新的 arrMax 值
}
baseVal[expo] = base; // 单项赋值
}
上方代码未作异常处理。这样做是不对的,但考虑到笔者水平有限,暂不设计这些内容。
多项式拷贝
该功能较简单。
/**
* 多项式拷贝。
*
* @param source 源多项式。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void Polynomial::set(Polynomial* source) {
// 如果目标的数组大小小于源多项式数组大小,则要申请更多空间存储内容。
if (arrMax < source->arrMax) {
long long* newArray;
newArray = (long long*)realloc(baseVal, (source->arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
arrMax = source->arrMax;
}
// 逐项复制
for (size_t i = 0; i <= arrMax; i++) {
if (i > source->arrMax) {
baseVal[i] = 0; // 如果这项超出源多项式范围,则赋为0.
}
else {
baseVal[i] = source->baseVal[i]; // 拷贝。
}
}
}
加法
逐项相加即可。
/**
* 多项式加法。
*
* @param pn 加“数”。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void Polynomial::add(Polynomial* pn) {
if (arrMax < pn->arrMax) {
long long* newArray = (long long*)realloc(baseVal, (pn->arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
for (size_t i = arrMax + 1; i <= pn->arrMax; i++) {
baseVal[i] = 0;
}
arrMax = pn->arrMax;
}
for (size_t i = 0; i <= arrMax && i <= pn->arrMax; i++) {
baseVal[i] += pn->baseVal[i]; // 逐项加。
}
}
乘法
本部分稍微复杂一些。
我们有多项式 f 和 g 如下:
f =
g =
做乘法,得到多项式 h = f · g, 如下:
h =
其中:
据此,便可设计多项式乘法功能。
/**
* 多项式乘法。
*
* @param pn 乘“数”。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void Polynomial::mul(Polynomial* pn) {
// 如果两个多项式中有一个为0多项式,则结果为0多项式。
if (pn->arrMax < 0 || arrMax < 0) {
for (size_t i = 0; i <= arrMax; i++) {
baseVal[i] = 0;
}
return;
}
// 将目标多项式原内容备份。
long long* tmp = (long long*) malloc((arrMax + 1) * sizeof(long long));
// 此处应对内存申请失败的情况做处理。本例暂认为该申请一定能成功。
for (size_t i = 0; i <= arrMax; i++) {
tmp[i] = baseVal[i];
}
size_t prevArrMax = arrMax; // 目标多项式原始数据大小。
if (arrMax < arrMax + pn->arrMax) {
long long* newArray;
newArray = (long long*) realloc(baseVal, (pn->arrMax + arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
arrMax = pn->arrMax + arrMax;
}
for (size_t i = 0; i <= arrMax; i++) {
baseVal[i] = 0; // 清空目标多项式的内容,以备后续写入。
// 注意:此处虽清空相乘的两个多项式之一的数据,但前方已将它备份到tmp中,故未造成数据丢失。
}
size_t i, j;
for (i = 0; i <= arrMax; i++) {
for (j = 0; j <= i; j++) {
if (j > prevArrMax || i - j > pn->arrMax) {
// 若超出两多项式之一的范围,则跳过。
continue;
}
baseVal[i] += tmp[j] * pn->baseVal[i - j];
}
}
}
输出
按照题意,如果多项式为空,则不输出。否则,按照规则输出多项式。
输出过程很简单,只需要循环读取baseVal
数组元素,对非0元素进行输出即可。
void Polynomial::print() {
for (int i = 0; i <= arrMax; i++) {
if (baseVal[i] != 0) {
printf("%lld %d ", baseVal[i], i);
}
}
}
清理
由于只有baseVal
涉及动态内存申请,所以清理部分释放baseVal
指向的内存即可。
void Polynomial::cleanup() {
free(baseVal);
}
多项式类组装
完成功能函数编写,即可获得完整的多项式类。如下:
/**
* 多项式类。
*/
struct Polynomial {
long long* baseVal = NULL;
size_t arrMax = -1;
/**
* 项更新。
*
* @param base 底数。
* @param expo 指数。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void update(int base, int expo) {
// 判断是否需要重新申请内存
if (expo > arrMax) {
// 需要重新申请内存
long long* newArray;
newArray = (long long*)realloc(baseVal, (expo + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
for (size_t i = arrMax + 1; i < expo; i++) {
baseVal[i] = 0; // 将新申请到的内容设为0.
}
arrMax = expo; // 记录新的 arrMax 值
}
baseVal[expo] = base; // 单项赋值
}
/**
* 多项式拷贝。
*
* @param source 源多项式。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void set(Polynomial* source) {
// 如果目标的数组大小小于源多项式数组大小,则要申请更多空间存储内容。
if (arrMax < source->arrMax) {
long long* newArray;
newArray = (long long*)realloc(baseVal, (source->arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
arrMax = source->arrMax;
}
// 逐项复制
for (size_t i = 0; i <= arrMax; i++) {
if (i > source->arrMax) {
baseVal[i] = 0; // 如果这项超出源多项式范围,则赋为0.
}
else {
baseVal[i] = source->baseVal[i]; // 拷贝。
}
}
}
/**
* 多项式加法。
*
* @param pn 加“数”。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void add(Polynomial* pn) {
if (arrMax < pn->arrMax) {
long long* newArray = (long long*)realloc(baseVal, (pn->arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
for (size_t i = arrMax + 1; i <= pn->arrMax; i++) {
baseVal[i] = 0;
}
arrMax = pn->arrMax;
}
for (size_t i = 0; i <= arrMax && i <= pn->arrMax; i++) {
baseVal[i] += pn->baseVal[i]; // 逐项加。
}
}
/**
* 多项式乘法。
*
* @param pn 乘“数”。
*
* @return (暂无。可考虑返回处理结果等)
* @throw (暂无)
*/
void mul(Polynomial* pn) {
// 如果两个多项式中有一个为0多项式,则结果为0多项式。
if (pn->arrMax < 0 || arrMax < 0) {
for (size_t i = 0; i <= arrMax; i++) {
baseVal[i] = 0;
}
return;
}
// 将目标多项式原内容备份。
long long* tmp = (long long*)malloc((arrMax + 1) * sizeof(long long));
// 此处应对内存申请失败的情况做处理。本例暂认为该申请一定能成功。
for (size_t i = 0; i <= arrMax; i++) {
tmp[i] = baseVal[i];
}
size_t prevArrMax = arrMax; // 目标多项式原始数据大小。
if (arrMax < arrMax + pn->arrMax) {
long long* newArray;
newArray = (long long*)realloc(baseVal, (pn->arrMax + arrMax + 1) * sizeof(long long));
if (newArray == NULL) {
// 如果申请失败...
// 进行一些处理,如抛出异常或结束程序。
}
else {
// 申请成功
baseVal = newArray;
}
arrMax = pn->arrMax + arrMax;
}
for (size_t i = 0; i <= arrMax; i++) {
baseVal[i] = 0; // 清空目标多项式的内容,以备后续写入。
// 注意:此处虽清空相乘的两个多项式之一的数据,但前方已将它备份到tmp中,故未造成数据丢失。
}
size_t i, j;
for (i = 0; i <= arrMax; i++) {
for (j = 0; j <= i; j++) {
if (j > prevArrMax || i - j > pn->arrMax) {
// 若超出两多项式之一的范围,则跳过。
continue;
}
baseVal[i] += tmp[j] * pn->baseVal[i - j];
}
}
}
void print() {
for (int i = 0; i <= arrMax; i++) {
if (baseVal[i] != 0) {
printf("%lld %d ", baseVal[i], i);
}
}
}
void cleanup() {
free(baseVal);
}
};
使用多项式类完成题目
拥有多项式类,即可轻松完成本题。main
函数如下:
int main() {
Polynomial pn[2], pnM;
int m, n, p, e;
int i, j;
for (int _k = 0; _k < 2; _k++) {
scanf("%d", &m);
for (i = 0; i < m; i++) {
scanf("%d%d", &p, &e);
pn[_k].update(p, e);
}
}
// 输入完毕。
int operationCode;
scanf("%d", &operationCode);
pnM.set(&pn[0]);
if (operationCode == 0) {
// add
pnM.add(&pn[1]);
pnM.print();
}
else if (operationCode == 1) {
// mul
pnM.mul(&pn[1]);
pnM.print();
}
else {
// add and mul
pnM.add(&pn[1]);
pnM.print();
pnM.set(&pn[0]);
pnM.mul(&pn[1]);
pnM.print();
}
}
总结
本题难度较低,对线性表的操作考验强度很弱。运算只需模拟。其中,执行乘法时,先创建缓存数组,将目标原始数据复制,然后清空目标数据,再模拟多项式相乘手算过程。