
大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~

解法 模拟 + 分类讨论,对于一元 n 次方程的最终结果,我们仅需按照顺序,考虑每一项的三件事情:符号 + 系数 + 次数


#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int n; cin >> n;
for(int i = n; i >= 0; i--)
{
//前置0
int a; cin >> a;
if(a == 0) continue;
//符号
if(a < 0) cout << '-';
else{
if(i != n) cout << '+';
}
//系数
a = abs(a);
if(a != 1 || (a == 1 && i == 0)) cout << a;
//次数
if(i == 0) continue;
else if(i == 1) cout << 'x';
else cout << "x^" << i;
}
return 0;
}补充一下代码中continue的作用分析
这段代码用于处理多项式的输出:
第一处:if(a == 0) continue;(第 12 行)
作用:当当前项的系数a为 0 时,跳过对该次项的所有处理,直接进入下一次循环。
原因:根据题目要求 “多项式中只包含系数不为 0 的项”,系数为 0 的项无需输出,因此用continue跳过后续的符号、系数、次数等处理步骤。
第二处:if(i == 0) continue;(第 22 行)
作用:当当前项的次数i为 0 时,跳过后续关于x的次数相关输出(如x或x^i)。
原因:次数为 0 的项是常数项(形式为a,不含x),在前面已经处理完系数的输出后,不需要再输出x相关的内容,因此用continue跳过第 23-24 行对x的处理代码。
再补充说一下,这里代码的逻辑明明是一项一项的系数输入进行处理的,在实际输入的时候却可以一次性把所有的系数输入进去的原因
核心原因是C++ 的输入流(cin)会自动处理输入缓冲区中的空白字符(空格、回车、制表符等),具体逻辑如下:
举例说明 假设n = 2(即需要输入 3 个系数,对应x²、x¹、x⁰项):
这两种方式对程序来说效果完全相同,因为cin会自动忽略空格和回车,按顺序从缓冲区中读取 3 个整数,分别在 3 次循环中赋值给a。

解法 模拟填数的过程 在一个矩阵中按照一定规律填数的通用解法:
因为题目中说每个数字都会占用3个字符,所以输出的格式是以%3d的格式输出

两个数组中每一对下标对应的两个数配套在一起使用就可以实现朝哪个位置偏移一个格子,而且这个顺序和该题目蛇形方阵的顺时针方向是一样的,若有题目是按逆时针排序的话,把数组中对应下标偏移的数据调整下就可以了,图中向量坐标x也y的方向是根据题目定义的,题目是从左上角开始的
补充:这里从下标为1开始处理有效数据是为了方便处理边界情况
#include<iostream>
using namespace std;
const int N = 15;
//定义右,下,左,上四个方向
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
int arr[N][N];//记录矩阵中每个位置的数
int main()
{
int n; cin >> n;
//模拟填数过程
int x = 1, y = 1;//初始位置
int cnt = 1;//当前位置要填的数
int pos = 0;//当前的方向
while(cnt <= n * n)
{
arr[x][y] = cnt;
//计算下一个位置
int a = x + dx[pos], b = y + dy[pos];
//判断是否越界
if(a < 1 || a > n || b < 1 || b > n || arr[a][b])
{
//更新出正确的该走的位置
pos = (pos + 1) % 4;
a = x + dx[pos], b = y + dy[pos];
}
x = a, y = b;
cnt++;
}
//输出
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
printf("%3d", arr[i][j]);
}
puts("");
}
return 0;
}
puts 是 C 语言标准库中的函数,它定义在 <stdio.h> 头文件中,用于输出字符串并自动在末尾添加换行符。 puts 函数用于输出字符串,它的特点是自动在字符串末尾添加换行符。


#include<iostream>
#include<algorithm>
using namespace std;
int p1, p2, p3, n;
string s;
string ret;
//判断是否是数字字符
bool isdig(char ch)
{
return ch >= '0' && ch <= '9';
}
//判断是否是小写字母
bool islet(char ch)
{
return ch >= 'a' && ch <= 'z';
}
//将[left, right]之间的字符展开
//left, right这两个字符不做处理
void add(char left, char right)
{
string t;
//遍历中间的字符
for(char ch = left + 1; ch < right; ch++)
{
char tmp = ch;
//处理p1
if(p1 == 2 && islet(tmp)) tmp -= 32;
else if(p1 == 3) tmp = '*';
//处理p2
for(int i = 0; i < p2; i++)
{
//先将展开的字符串放到临时字符串t中
//后面可能逆序,逆序之后再加入ret中
t += tmp;
}
}
//处理p3
if(p3 == 2) reverse(t.begin(), t.end());
ret += t;
}
int main()
{
cin >> p1 >> p2 >> p3 >> s;
int n = s.size();
//循环遍历每一个字符
for(int i = 0; i < n; i++)
{
char ch = s[i];
//减号出现在第一位或最后一位无法展开,也不作处理
if(s[i] != '-' || i == 0 || i == n - 1) ret += ch;
else{
char left = s[i - 1], right = s[i + 1];
//判断是否展开
if(isdig(left) && isdig(right) && right > left ||
islet(left) && islet(right) && right > left)
{
//展开
add(left, right);
}else{
ret += ch;
}
}
}
cout << ret << endl;
return 0;
}代码中没有明确写出(5)对减号右边的字符恰好是左边字符的后继的处理情况,其实是隐含在内的
代码处理逻辑解析 当减号右边的字符是左边字符的后继时(即right = left + 1,如d和e、3和4),代码的处理流程如下:
由于 “右边是左边的后继” 满足right = left + 1 > left,且必然同类型(数字或字母),因此会进入 “展开” 分支,执行add(left, right)函数,且不会将减号本身加入结果(ret)。
add函数的核心处理 add函数的作用是生成左右字符之间的展开内容(不包含left和right本身),其关键循环为:
for(char ch = left + 1; ch < right; ch++)当right = left + 1时,循环条件ch < right等价于ch < left + 1,而ch的初始值是left + 1,因此循环不会执行,临时字符串t保持为空。
因此,left和right会直接拼接,中间没有减号,也没有其他字符,恰好实现了 “删除减号” 的效果。
当数据的值特别大,各种类型都存不下的时候,此时就要用高精度算法来计算加减乘除:
高精度算法本质上还是模拟算法,用代码模拟小学列竖式计算加减乘除的过程


解法 模拟小学列竖式计算两数相加的过程

该题目数组数组大小是小于等于500,但是实际情况下高精度加法是可以计算到1e6级别(不会超时),所以这里数组的大小可以扩展到1e6级别
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], b[N], c[N];//第一个数,第二个数,最终结果
int la, lb, lc;//分别标记三个数组中数的长度
//高精度加法模板
void add(int c[], int b[], int a[])
{
for (int i = 0; i < lc; i++)
{
c[i] += a[i] + b[i];//对应位相加 + 进位
c[i + 1] += c[i] / 10;//处理进位
c[i] %= 10;//处理余数
}
//lc的长度有可能是二者长度的最大值 + 1
if (c[lc]) lc++;
}
int main()
{
string x, y; cin >> x >> y;
//拆分每一位,逆序放入数组
la = x.size(); lb = y.size(); lc = max(la, lb);
for (int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
for (int i = 0; i < lb; i++) b[lb - 1 - i] = y[i] - '0';
//模拟手工加法的过程
add(c, b, a);
//逆序输出结果
for (int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;
}

注意题目条件,第二个数可能要比第一个数大,这时候就要用较大的数减去较小的数再添加一个负号

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;
//比大小
bool cmp(string& x, string& y)
{
//先比较长度
if(x.size() != y.size()) return x.size() < y.size();
//再按字典序比较
return x < y;
}
//高精度减法模板
void sub(int c[], int a[], int b[])
{
for(int i = 0; i < lc; i++)
{
//对应位相减 + 处理借位
c[i] += a[i] - b[i];
if(c[i] < 0)
{
//借位
c[i + 1] -= 1;
c[i] += 10;
}
}
//处理前导0
while(lc > 1 && c[lc - 1] == 0) lc--;
}
int main()
{
string x, y; cin >> x >> y;
//比较大小
if(cmp(x, y))
{
swap(x, y);
cout << '-';
}
//分别拆分,逆序存入数组中
la = x.size(); lb = y.size(); lc = max(la, lb);
for(int i = 0; i < la; i++) a[la - i - 1] = x[i] - '0';
for(int i = 0; i < lb; i++) b[lb - i - 1] = y[i] - '0';
//模拟手工减法 c = a - b
sub(c, a, b);
//正序输出
for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;
}


补充一个点: 在 C++ 中,数组的默认初始化行为取决于其定义的位置,并非所有情况都是 0 或随机值: 1. 全局 / 静态数组 代码中的 int a[N], b[N], c[N]; 是定义在全局作用域(main 函数外)的数组,全局变量和静态变量会被编译器自动初始化为0(属于 “零初始化”)。 这是因为全局 / 静态变量存储在静态存储区(.data 段或.bss 段),C++ 标准规定:未显式初始化的全局 / 静态基本类型变量(如 int)会被默认初始化为 0。 2. 局部数组(函数内定义) 如果数组定义在函数内(如 main 里)且未显式初始化,那么它的元素是未定义值(随机的 “垃圾值”),因为局部变量存储在栈区,栈区不会自动初始化变量。 3. 且在 C++ 中,所有定义在全局作用域(函数 / 类外部)的变量,以及用static修饰的局部变量,都会被编译器执行零初始化: 对于整数类型(如int),默认值为0; 对于浮点类型(如double),默认值为0.0; 对于指针类型,默认值为nullptr(C++11 及以后)或NULL。


核心思路: 无进位相乘再相加
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;
//模拟乘法的模板
void mul(int c[], int a[], int b[])
{
//无进位相乘,然后相加
for(int i = 0; i < la; i++)
{
for(int j = 0; j < lb; j++)
{
c[i + j] += a[i] * b[j];
}
}
//处理进位
for(int i = 0; i < lc; i++)
{
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
//处理前置0
while(lc > 1 && c[lc - 1] == 0) lc--;
}
int main()
{
string x, y; cin >> x >> y;
//分别拆分每一位,逆序存入数组中
la = x.size(); lb = y.size(); lc = la + lb;
for(int i = 0; i < la; i++) a[la - i - 1] = x[i] - '0';
for(int i = 0; i < lb; i++) b[lb - i - 1] = y[i] - '0';
//模拟手工乘法 c = a * b
mul(c, a, b);
//正序打印
for(int i = lc - 1; i >=0; i--) cout << c[i];
return 0;
}

解法: 模拟小学列竖式计算两数相除的过程(这里其实是高精度 / 低精度)
定义一个指针 i 从高位遍历被除数,一个变量 t 标记当前被除的数,记除数是 b
被除数遍历完毕之后,t 里面存的就是余数,但是商可能存在前导0,注意清空

#include<iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
int a[N], b, c[N];
int la, lc;
//高精度除法的模板 c = a / b (高精度 / 低精度)
void sub(int c[], int a[], int b)
{
//标记每次除完之后的余数
LL t = 0;
for(int i = la - 1; i >= 0; i--)
{
//计算当前被除数
t = t * 10 + a[i];
c[i] = t / b;
t %= b;
}
//处理前导0
while(lc > 1 && c[lc - 1] == 0) lc--;
}
int main()
{
string x; cin >> x >> b;
//拆分每一位,逆序存入数组
la = x.size();
for(int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
//模拟手工除法 c = a / b;
lc = la;
sub(c, a, b);
for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;
}注意:余数是有可能超过int的范围,因为b是小于等于109,如果余上一个10的9次方的数,再×10就会变成10的十次方,就超过了int的范围