题目
注意
- 数据类型请使用int,本题中所有运算的结果均视作对int型自然溢出
- 各操作需在稀疏矩阵上进行,充分考虑数据的稀疏性, 不得直接或间接转换为二维数组形式计算 ,否则取消成绩
题目描述
- 创建 稀疏矩阵类 (参照课本 MatrixTerm 三元组定义) ,采用行主顺序把稀疏矩阵非0元素映射到一维数组中,提供操作:两个稀疏矩阵相加、两个稀疏矩阵相乘、稀疏矩阵的转置、输出矩阵。
- 键盘输入矩阵的行数、列数;并按行优先顺序输入矩阵的各元素值,建立矩阵;
- 对建立的矩阵执行相加、相乘、转置的操作,输出操作的结果矩阵。
操作描述
- 为方便操作描述,我们假设存在一个矩阵 P,下列各操作实际为对矩阵 P 的操作。
- 重置矩阵 :
1
矩阵的行数n 矩阵的列数m
[n行m列 表示矩阵中的所有元素]
即重置矩阵 P 的尺寸为 n 行 m 列,且随后按行优先顺序输入矩阵 P 的各个元素。 - 矩阵乘法:
2
矩阵的行数 矩阵的列数
矩阵中非零元素个数t
[t行 表示矩阵中非零元素]
t 行非零元素已按行优先顺序给出,矩阵中非零元素的表示为x y v
,其中x
表示行序号,y
表示列序号,v
表示非零元素值,行列序号从 1 开始。
设输入的矩阵为 Q,若 PxQ 运算合法,则将 PxQ 的结果矩阵赋给 P,若不合法,则将 Q 赋给 P,同时输出 -1。 - 矩阵加法:
3
矩阵的行数 矩阵的列数
矩阵中非零元素个数t
[t行 表示矩阵中非零元素]
t 行非零元素已按行优先顺序给出,矩阵中非零元素的表示为x y v
,其中x
表示行序号,y
表示列序号,v
表示非零元素值,行列序号从 1 开始。
设输入的矩阵为 Q,若 P+Q 运算合法,则将 P+Q 的结果矩阵赋给 P,若不合法,则将 Q 赋给 P,同时输出 -1。 - 输出操作:
4
设当前矩阵 P 的尺寸为 n 行 m 列,第一行输出矩阵 P 的行数和列数,随后 n 行按行优先顺序输出矩阵 P,每行 m 个数字,来表示当前的矩阵内容,每行数字之间用空格分隔。 - 转置操作:
5
设当前矩阵 P 的尺寸为 n 行 m 列,将其转置为 m 行 n 列的矩阵,无需输出。
格式
- 输入
第一行一个 w 代表操作个数,接下来若干行是各个操作,其中保证第一个操作一定为重置矩阵。
- 输出
当执行操作 4 时,输出矩阵 P;当执行操作 2 或 3 时,若对应运算不合法,则输出 -1。
数据范围
思路
整体思路:
- 定义一个矩阵三元组结构体,用于表示非零元素的行索引、列索引、值
- 定义一个稀疏矩阵类,用来实现矩阵的各种操作,包括重置、相乘、相加、输出、转置
- 定义主函数,按照要求进行选择操作
具体思路:
基本函数
- 构造函数:
- 初始化稀疏矩阵的行数、列数、非零元素个数,并为矩阵三元组线性表元素动态分配存储空间
- 析构函数:释放构造函数中分配的内存空间
- 重载运算符
==
:将一个稀疏矩阵赋给另一个稀疏矩阵 - 输入函数
input
:- 实现将非零元素的行索引、列索引的值输入到当前稀疏矩阵中
- 首先创建一个三元组对象,并将参数赋给相应的变量
- 然后将这个三元组对象添加到稀疏矩阵的element数组中,逐步构建稀疏矩阵的非零元素
重置reset
- 按行优先顺序,从标准输入中读取矩阵元素
- 将非零元素以行优先顺序储存在类的成员变量一维数组中
- 非零元素个数++
相乘
- 首先进行判断相乘的合法性(
m
×
n
∗
p
×
q
m×n*p×q
m×n∗p×q):
n=p?
- 若不满足条件,输出-1,并返回;
- 创建一个结果矩阵result,其行数、列数分别为当前矩阵的行数、输入矩阵的列数:
sparseMatrix result(rows,Q.cols);
- 创建三个辅助数组,用于辅助计算乘积结果
- count:输入矩阵(Q)每一行非零元素个数。
- index:Q每一行第一个非零元素的索引
- answer:储存每次乘法运算的结果
- 创建之后,分别先将三个数组进行初始化。
- 初始准备工作做好后,就进行矩阵相乘。
- 遍历当前矩阵(P)的行和Q的列
- 在P的第i行和Q的第j列的乘法运算中,遍历P中第i行非零元素
- 对于每个非零元素,根据索引c,从Q中对应列的第一个非零元素开始遍历
- 将P中的非零元素的值与Q中对应的值相乘,并累加到answer数组中对应的列上
- 将乘积结果中的非零元素储存在result矩阵的element数组中,并更新非零元素数量
- 将result赋给P
- 释放内存
相加
- 判断相加合法性
- 若可以,创建一个结果矩阵result,其行数、列数与当前矩阵相同
- 初始化两个索引变量,用来遍历P、Q的非零元素
- 进行相加运算:在循环中,比较P和Q中每个非零元素的索引值
- P索引<Q索引,说明P的索引在Q中对应的数为0,则将当前矩阵P的非零元素添加到结果矩阵 result 中,并递增 it 和 result.terms
- 如果两个矩阵的非零元素索引值相等,则将两个非零元素的值相加
- 如果结果不为零,则将其添加到结果矩阵 result 中,并递增 it、ib 和 result.terms。
- 如果 Q 的非零元素索引值小于当前矩阵P的非零元素索引值,则将 Q 的非零元素添加到结果矩阵 result 中,并递增 ib 和 result.terms。
- 循环结束后,复制剩余的非零元素到结果矩阵 result 中。
- 将结果矩阵 result 赋值P
输出
- 先输出行列数
- 使用两层嵌套循环遍历矩阵的每个元素位置。
- 对于每个位置 (i, j),首先检查变量 k 是否小于非零元素的个数 terms,以确保还有未遍历的非零元素。
- 如果当前非零元素的行索引等于 i+1(因为索引从0开始,而行号从1开始),并且列索引等于 j+1,则输出当前非零元素的值,并递增 k。
- 如果当前位置不是非零元素的位置,即当前非零元素的行列索引与当前位置不匹配,则输出0。
- 在内层循环结束后,输出一个换行符,表示已输出完当前行的元素。
矩阵转置
- 首先,创建一个新的稀疏矩阵 b,其行、列数与当前矩阵相反,非零元素个数相等
- 创建两个辅助数组 colSize 和 rowNext,用于实现转置操作。
- colSize 数组的下标表示当前矩阵的每一列,存储的值表示对应列的非零元素个数。
- rowNext 数组的下标表示转置后矩阵的每一行,存储的值表示对应行的起始位置(在转置后的矩阵中的索引位置)。
- 遍历当前矩阵的每一列,计算每列的非零元素个数,并存储在 colSize 数组中。
- 使用动态规划的思想,计算转置后每一行的起始位置。
- 初始化 rowNext[1] 为0,表示转置后的第一行起始位置为0。
- 对于每个列 i(从第二列开始),通过累加前一列的非零元素个数来计算当前列的起始位置,存储在 rowNext[i] 中。
- 进行转置复制操作,将当前矩阵的非零元素复制到转置后的矩阵 b 中。
- 遍历当前矩阵的每个非零元素,获取其原始元素的行列索引和值。
- 根据转置后的行列索引,将元素复制到转置矩阵 b 的对应位置。
- 根据 rowNext 数组的值,对转置矩阵 b 中的非零元素进行逐行排序。
- 将转置后的矩阵 b 赋值给当前矩阵对象,以更新当前矩阵的内容。
AC代码
#include<iostream>
using namespace std;
const int SIZE=1000000;
//矩阵三元组定义
struct matrixTerm
{
int row;
int col;
int value;
};
//稀疏矩阵类
class sparseMatrix
{
public:
sparseMatrix(int r,int c);//构造函数
~sparseMatrix(){delete[] element;}//析构函数
void reset(int n,int m);//1重置矩阵
void multiply(sparseMatrix &Q);//2两个稀疏矩阵相乘,结果存放于P
void add(sparseMatrix &Q);//3两个稀疏矩阵相加,结果存放于P
void output();//4输出矩阵
void transpose();//5矩阵转置
sparseMatrix& operator=(sparseMatrix& a);//重载=,用得到
void input(int x,int y,int v);//输入非0
private:
int rows;//矩阵行数
int cols;//矩阵列数
int terms;//非0元素个数
matrixTerm* element;//标记非零元素的行索引、列索引、值
};
//构造函数
sparseMatrix::sparseMatrix(int r,int c){
rows=r;
cols=c;
terms=0;
element=new matrixTerm[SIZE];
}
//更新三元组
void sparseMatrix::input(int x,int y,int v){
matrixTerm a;
a.row=x;
a.col=y;
a.value=v;
element[terms]=a;
terms++;
}
//1重置矩阵
void sparseMatrix::reset(int n,int m){
rows=n;
cols=m;
terms=0;
for(int i=1;i<=rows;i++){//行列的0都不用,从1开始
for(int j=1;j<=cols;j++){
int number;
cin>>number;
if(number!=0){//非零元素用按行优先顺序用一维数组记录
element[terms].row=i;
element[terms].col=j;
element[terms].value=number;
terms++;
}
}
}
}
//重载=
sparseMatrix& sparseMatrix::operator=(sparseMatrix& a)
{//运算符重载 ,赋值*this = a
rows = a.rows;
cols = a.cols;
terms = a.terms;
for (int i = 0; i < terms; i++)
element[i] = a.element[i];
return *this;
}
//2两个稀疏矩阵相乘,结果存放于P
void sparseMatrix::multiply(sparseMatrix &Q){
//判断是否能相乘
if(cols!=Q.rows){//不合法
*this=Q;//将Q赋给P
cout<<"-1"<<endl;
return ;
}
else{
sparseMatrix result(rows,Q.cols);//保存结算结果
int *count=new int[10000];//每一行非零元素的个数
int *index=new int[10000];//第一个非零元素的索引
int *answer=new int[10000];//结果
//初始化
for(int i=1;i<=Q.rows;i++)
count[i]=0;
//统计每一行的非零元素个数
for(int i=0;i<Q.terms;i++){
if(Q.element[i].value!=0)
count[Q.element[i].row]++;
}
//计算第一个非零元素索引
index[1]=0;
for(int i=2;i<=Q.terms;i++){
index[i]=index[i-1]+count[i-1];//用前一行的非零元素索引加上前一行非零元素的个数
}
//矩阵相乘
int rIndex=0;//索引当前矩阵非零元素
for(int i=1;i<=rows && rIndex<terms;i++){
for(int j=1;j<=Q.cols;j++)
answer[j]=0;//初始化
while(rIndex<terms && element[rIndex].row==i){//找到当前行的非零元素
int c=element[rIndex].col;
if(count[c]!=0){//说明Q列有非零元素
for(int a=index[c];a<index[c]+count[c];a++)//从第一个非零元素开始
answer[Q.element[a].col]+=element[rIndex].value*Q.element[a].value;
}
rIndex++;
}
for(int k=1;k<=Q.cols;k++){//进行储存非零元素
if(answer[k]!=0){
result.element[result.terms].row=i;
result.element[result.terms].col=k;
result.element[result.terms].value=answer[k];
result.terms++;
}
}
}
*this=result;
delete[] count;
delete[] index;
delete[] answer;
}
}
//3两个稀疏矩阵相加,结果存放于P
void sparseMatrix::add(sparseMatrix &Q){
//判断是否能相加
if(Q.rows!=rows || Q.cols !=cols){
*this=Q;
cout<<"-1"<<endl;
return;
}
else{
sparseMatrix result(rows,cols);
int it=0,ib=0;
while(it!=terms && ib!=Q.terms){
//每一行元素的行主索引+列数
int tIndex=element[it].row*cols+element[it].col;
int bIndex=Q.element[ib].row*cols+Q.element[ib].col;
if(tIndex<bIndex){//Q的元素在后
result.element[result.terms].col=element[it].col;
result.element[result.terms].row=element[it].row;
result.element[result.terms].value=element[it].value;
it++;
result.terms++;
}
else {
if(tIndex==bIndex){//两项在同一个位置
if(element[it].value+Q.element[ib].value!=0){
result.element[result.terms].col=element[it].col;
result.element[result.terms].row=element[it].row;
result.element[result.terms].value=element[it].value+Q.element[ib].value;
result.terms++;
}
it++;
ib++;
}
else{//Q的索引在前
result.element[result.terms].col=Q.element[ib].col;
result.element[result.terms].row=Q.element[ib].row;
result.element[result.terms].value=Q.element[ib].value;
ib++;
result.terms++;
}
}
}
//复制剩余元素
for(;it!=terms;it++)
{
result.element[result.terms].col = element[it].col;
result.element[result.terms].row = element[it].row;
result.element[result.terms].value = element[it].value;
result.terms++;
}
for(;ib != Q.terms;ib++)
{
result.element[result.terms].col = Q.element[ib].col;
result.element[result.terms].row = Q.element[ib].row;
result.element[result.terms].value = Q.element[ib].value;
result.terms++;
}
*this = result;
}
}
//4输出矩阵
void sparseMatrix::output(){
cout<<rows<<" "<<cols<<endl;
int k=0;
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
if(k<terms && element[k].row==i+1 && element[k].col==j+1){//确保有未遍历的非零元素,并且行列索引对应匹配
cout<<element[k].value<<" ";
k++;
}
else
cout<<0<<" ";
}
cout<<endl;
}
}
//5矩阵转置
void sparseMatrix::transpose(){
sparseMatrix b(cols,rows);
b.terms=terms;
int * colSize=new int[cols+1];//原矩阵每列非零元素个数
int * rowNext=new int[cols+1];//转置后每一行的索引位置
//计算*this每一列的非零元素
for(int i=1;i<=cols;i++)
colSize[i]=0;
for(int i=0;i<terms;i++)
colSize[element[i].col]++;
//求出b中每一行的第一个非零元素索引
rowNext[1]=0;
for(int i=2;i<=cols;i++)
rowNext[i]=rowNext[i-1]+colSize[i-1];
//实施从*this到b的转置复制
for(int i=0;i<terms;i++)
{
int j=rowNext[element[i].col];//转置后矩阵中当前非零元素的行索引
//行列转置
b.element[j].row = element[i].col;
b.element[j].col = element[i].row;
b.element[j].value = element[i].value;
rowNext[element[i].col] = rowNext[element[i].col] + 1;//以便在同一列中继续处理下一个非零元素的位置
}
*this = b;
}
int main(){
sparseMatrix P(0,0);
int w,m,n,t;
int choice;
cin>>w;
for(int i=0;i<w;i++){
cin>>choice;
if(choice==1)
{
cin>>n>>m;
P.reset(n,m);
}
else if(choice==2){
cin>>n>>m;
cin>>t;
sparseMatrix Q1(n,m);
for(int i=0;i<t;i++){
int x,y,v;
cin>>x>>y>>v;
Q1.input(x,y,v);
}
P.multiply(Q1);
}
else if(choice==3){
cin>>n>>m;
cin>>t;
sparseMatrix Q2(n,m);
for(int i=0;i<t;i++){
int x,y,v;
cin>>x>>y>>v;
Q2.input(x,y,v);
}
P.add(Q2);
}
else if(choice==4){
P.output();
}
else if(choice==5)
P.transpose();
}
return 0;
}