7-1 数列查询
原题呈现:
解题思路: 该题时间给的比较紧,只有10ms,因而用打表的方法解决。
以下是根据这一思路写出的代码:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
long long a[10000];
int main() {
a[0] = 10;
int n,x;
scanf("%d", &n);
for (int i = 1; i < 10000; i++) {
a[i] = a[i - 1] * 11 / 10;
}
for (int i = 0; i < n; i++) {
scanf("%d", &x);
printf("%d\n", a[x-1]);
}
return 0;
}
解后反思: 由于题目给出查询的值在int范围内,因而可以算出自变量的上限:由10*(11/10)n<231,可求出n<203,也就是自变量的上限为202。
7-2 稀疏矩阵之和
原题呈现:
解题思路: 用三元组存储稀疏矩阵。首先判断两个矩阵行、列数是否相等(即是否可以相加)。然后比较非零数值所在的行数、列数大小,以此决定将其中的哪一个放入表示结果的三元组(比较及分类讨论过程详见代码注释)。
以下是根据这一思路写出的代码:
#include <stdio.h>
#define MaxSize 50000//矩阵中非零元素最多个数
typedef struct
{
int r; //行数
int c; //列数
int d; //数据元素
}TupNode; //三元组定义
typedef struct
{
int row; //行数值
int cols; //列数值
int nums; //非零元素个数
TupNode data[MaxSize]; //存放数组
}TSMatrix; //三元组顺序表定义
//输出三元组
void DipMat(TSMatrix t)
{
int i;
printf("%d %d %d\n", t.row, t.cols, t.nums);
if (t.nums <= 0)//判断是否有非零元素
return;
for (i = 0; i < t.nums; i++)
printf("%d %d %d\n", t.data[i].r, t.data[i].c, t.data[i].d);
}
//两个稀疏矩阵相加后对应的三元组
void MatAdd(TSMatrix a, TSMatrix b, TSMatrix& c)
{
if (a.row != b.row || a.cols != b.cols)//判断是否符合矩阵相加条件,即两矩阵行数和列数分别相等
{
printf("Illegal!");
return;
}
c.row = a.row; //总行数赋值
c.cols = a.cols; //总列数赋值
int i = 0, j = 0, k = 0;
while (i < a.nums || j < b.nums) //遍历两个三元组
{
if (i < a.nums && j < b.nums && a.data[i].r < b.data[j].r)//比较非零数值所在的行数大小,将较小的行数的非零数值放进c的三元组
{
c.data[k].r = a.data[i].r;
c.data[k].c = a.data[i].c;
c.data[k].d = a.data[i].d;
i++;
k++;
}
else if (i < a.nums && j < b.nums && a.data[i].r == b.data[j].r)//比较非零数值所在的行数大小,相等时
{
if (a.data[i].c < b.data[j].c)//比较非零数值所在的列数大小,将较小的列数非零数值放进c的三元组
{
c.data[k].r = a.data[i].r;
c.data[k].c = a.data[i].c;
c.data[k].d = a.data[i].d;
i++;
k++;
}
else if (i < a.nums && j < b.nums && a.data[i].c == b.data[j].c)//比较非零数值所在的列数大小,将两个非零元素相加的值放进c的三元组
{
c.data[k].r = b.data[j].r;
c.data[k].c = b.data[j].c;
c.data[k].d = a.data[i].d + b.data[j].d;
i++;
j++;
if (c.data[k].d != 0)
k++;
}
else if (i < a.nums && j < b.nums) //比较非零数值所在的列数大小,将较小的列数非零数值放进c的三元组
{
c.data[k].r = b.data[j].r;
c.data[k].c = b.data[j].c;
c.data[k].d = b.data[j].d;
j++;
k++;
}
}
else if (i < a.nums && j < b.nums) //比较非零数值所在的行数大小,将较小的行数数非零数值放进c的三元组
{
c.data[k].r = b.data[j].r;
c.data[k].c = b.data[j].c;
c.data[k].d = b.data[j].d;
j++;
k++;
}
else if (i == a.nums) {
c.data[k].r = b.data[j].r;
c.data[k].c = b.data[j].c;
c.data[k].d = b.data[j].d;
j++;
k++;
}
else if (j == b.nums) {
c.data[k].r = a.data[i].r;
c.data[k].c = a.data[i].c;
c.data[k].d = a.data[i].d;
i++;
k++;
}
}
c.nums = k;//非零元素个数
}
//返回三元组 t 表示的 A[i][j]值
int getvalue(TSMatrix t, int i, int j)
{
for (int k = 0; k < t.nums; k++)
{
if (t.data[k].r == i && t.data[k].c == j)
return t.data[k].d;
}
return 0;
}
int a[1000][1000], b[1000][1000];
int main()
{
TSMatrix t1, t2, c; //t1, t2为三元组
int tmpr, tmpc, tmpd, tmpnumber = 0, sum;
scanf("%d %d %d", &t1.row, &t1.cols, &sum);
for(int i = 0; i < sum; i++) {
scanf("%d %d %d", &tmpr, &tmpc, &tmpd);
if (tmpr > t1.row) {
printf("Illegal!");
return 0;
}
t1.data[tmpnumber].r = tmpr;
if (tmpr > t1.cols) {
printf("Illegal!");
return 0;
}
t1.data[tmpnumber].c = tmpc;
t1.data[tmpnumber].d = tmpd;
tmpnumber++;
}
t1.nums = tmpnumber;
tmpnumber = 0;
scanf("%d %d %d", &t2.row, &t2.cols, &sum);
for (int i = 0; i < sum; i++) {
scanf("%d %d %d", &tmpr, &tmpc, &tmpd);
t2.data[tmpnumber].r = tmpr;
t2.data[tmpnumber].c = tmpc;
t2.data[tmpnumber].d = tmpd;
tmpnumber++;
}
t2.nums = tmpnumber;
tmpnumber = 0;
MatAdd(t1, t2, c); //计算两个稀疏矩阵相加后对应的三元组
DipMat(c); //输出三元组
return 0;
}
解后反思: 博主在上机时没有考虑到两个矩阵对应位置的加和可能为0,在这种情况下就不用把和保存在三元组中。这道题本身不算很难,但需要考虑的点比较多。
7-3 文字编辑
原题呈现:
错误解题思路: 用循环链表保存数据并通过对循环链表的操作模拟题中的各个过程。
以下是根据错误解题思路写出的代码:
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <iostream>
using namespace std;
typedef struct x {
int num;
struct x* next;
}xtype;
typedef xtype* pxtype;
pxtype head, rear, p, p0, q, q0;
int main() {
int t, n, m, i, j;
int k, kk;
char s;
scanf("%d", &t);
for (k = 0; k < t; k++) {
scanf("%d %d", &n, &m);
for (kk = 0; kk < n; kk++) {
pxtype p;
p = (pxtype)malloc(sizeof(xtype));
p->num = kk + 1;
if (kk == 0) {
head = p;
rear = p;
p->next = head;
}
else {
rear->next = p;
rear = p;
p->next = head;
}
}
p0 = head;
p = head;
q0 = head;
q = head;
for (kk = 0; kk < m; kk++) {
p0 = head;
p = head;
q0 = head;
q = head;
char ch;
while ((ch = getchar()) != '\n' && ch != EOF) {};
scanf("%c %d %d", &s, &i, &j);
if (s == 'A') {
while (p->num != i) {
p0 = p;
p = p->next;
}
if (p == head){
head = head->next;
q0 = head;
q = head;
}
p0->next = p->next;
free(p);
while (q->num != j) {
q0 = q;
q = q->next;
}
pxtype newp;
newp = (pxtype)malloc(sizeof(xtype));
newp->num = i;
q0->next = newp;
newp->next = q;
}
else if (s == 'B') {
while (p->num != i) {
p0 = p;
p = p->next;
}
if (p == head) {
head = head->next;
q0 = head;
q = head;
}
p0->next = p->next;
free(p);
while (q->num != j) {
q0 = q;
q = q->next;
}
q0 = q;
q = q->next;
pxtype newp;
newp = (pxtype)malloc(sizeof(xtype));
newp->num = i;
q0->next = newp;
newp->next = q;
}
else if (s == 'Q') {
while (p->num != j) {
p0 = p;
p = p->next;
}
if (i == 0)
printf("%d\n", p0->num);
else if (i == 1)
printf("%d\n", p->next->num);
}
}
}
return 0;
}
错因分析: 每次需要定位一个字符时都需要从头开始遍历,时间复杂度过高。
改进版解题思路: 用双向循环链表保存数据,为了写起来简便采取静态链表形式。用类似“跳舞”的方法模拟题目中的移动操作。
以下是根据这一思路写出的代码:
#include <stdio.h>
int left[10000], right[10000];
int main() {
int t;
scanf("%d", &t);
for(int k=0;k<t;k++) {
int n, q;
scanf("%d %d", &n, &q);
for (int kk = 1; kk <= n; kk++) {
left[kk] = kk - 1;
right[kk] = kk + 1;
}
left[1] = n;
right[n] = 1;
for(int kk=0;kk<q;kk++) {
int i, j;
char s,ch;
while ((ch = getchar()) != '\n' && ch != EOF) {};
scanf("%c %d %d",&s, &i, &j);
if (s == 'A') {
right[left[i]] = right[i];
left[right[i]] = left[i];
right[i] = j;
left[i] = left[j];
right[left[i]] = i;
left[right[i]] = i;
}
else if (s == 'B') {
right[left[i]] = right[i];
left[right[i]] = left[i];
left[i] = j;
right[i] = right[j];
right[left[i]] = i;
left[right[i]] = i;
}
else if (s=='Q') {
if (i == 0) {
printf("%d\n",left[j]);
}
else if(i==1) {
printf("%d\n", right[j]);
}
}
}
}
return 0;
}
解后反思: 较之于循环链表,静态版本的双向循环链表写起来不很复杂,同时在很多情况下能比较有效地降低时间复杂度。
7-4 幸福指数
原题呈现:
解题思路: 用纯暴力遍历的话时间复杂度过高,会超出时间限制。因而考虑对每一个数,找出其左边和右边第一个比其小的数(这里可以利用单调栈在线性时间内求出并保存在数组中),由此得出对于每一个数的最大幸福指数。再通过比较得出所有数最大幸福指数的最大值即可。
以下是根据这一思路写出的代码:
#include <iostream>
#include <stack>
using namespace std;
int num[100010];
long long numsum[100010];
stack<int> sl;
stack<int> sr;
int sleft[100010],sright[100010];
int main(void)
{
int n, i, l = 0, r = 0, tmpl, tmpr;
long long max = 0, tmpmax;
cin >> n;
for (i = 0; i < n; i++) {
scanf("%d", &num[i]);
if (i == 0)
numsum[i] = num[i];
else if (i != 0)
numsum[i] = numsum[i - 1] + num[i];
}
for (i = 0; i < n; i++) {
if (sl.empty()) {
sleft[i] = -1;
sl.push(i);
continue;
}
while (!sl.empty() && num[sl.top()] >= num[i])
sl.pop();
if (!sl.empty())
sleft[i] = sl.top();
else
sleft[i] = -1;
sl.push(i);
}
for (i = n-1; i >= 0; i--) {
if (sr.empty()) {
sright[i] = -1;
sr.push(i);
continue;
}
while (!sr.empty()&&num[sr.top()] >= num[i])
sr.pop();
if (!sr.empty())
sright[i] = sr.top();
else
sright[i] = -1;
sr.push(i);
}
for (i = 0; i < n; i++) {
if (sleft[i] == -1)
tmpl = 1;
else
tmpl = sleft[i] + 1 +1;
if (sright[i] == -1)
tmpr = n;
else
tmpr = sright[i];
if(tmpl==1)
tmpmax = numsum[tmpr-1] * num[i];
else
tmpmax = (numsum[tmpr-1]-numsum[tmpl-2]) * num[i];
if (tmpmax > max) {
l = tmpl;
r = tmpr;
max = tmpmax;
}
else if (tmpmax == max && (tmpr - tmpl) > r - l) {
l = tmpl;
r = tmpr;
}
}
printf("%lld\n", max);
printf("%d %d", l, r);
return 0;
}
解后反思: 博主对单调栈的掌握不够熟练,因而上机时面对实际题目情境根本不能联想到单调栈的知识。