步骤
:
- 完成题意和样例的输入和输出(编程思维,遇到一个题如何拆开)
- ——对
- 把程序放到官网上去验证(算法思维,对前面写出的程序进行优化
- ——快
思路分析
- 数据存储
- 放到一维数组里
- 程序分析
- 找到最小值的下标
- 在最小值的左右两个位置加上最小值
- 将删除的值改为-1
- 套一层循环
代码示范
- 先写一部分代码
- 进行单元测试,写出部分逻辑,避免出现错误,使验证每一步都是正确的
- 每写一步检测对不对,对了再写下一步
//物理上删除数组中的元素
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
//第一步,输入
int N, M;
cin >> N >> M;
//scanf("%d %d", &N, &M);
int buff[N] = {0};
for (int i = 0; i < N; i++)
{
cin >> buff[i];
//scanf("%d", buff+i);
}
/*单元测试,验证输入的正确性*/
//cout << N << ", " << M << endl;
//for (int i = 0; i < N; i++)
//{
// cout << buff[i] << " ";
//}
for (int i = 0; i < M; i++)
{
//第二步:找到数组中最小元素的下标
int minId = 0;
for (int j = 0; j < N - i; j++)
{
if (buff[minId] > buff[j])
{
minId = j;
}
}
// 单元测试2,验证找到的数组中最小的元素和其下标
//cout << buff[minId] << " " << minId << endl;
//第三步:最小元素下标左右都加上最小元素
if (minId - 1 > -1)
buff[minId - 1] += buff[minId];
if (minId + 1 < N)
buff[minId + 1] += buff[minId];
//第四步:删除最小元素(物理上删除)
for (int j = minId; j < N - i; j++) //N-i代表删除了i个元素
{
buff[j] = buff[j + 1];
}
}
//第五步:输出数组剩下的元素
for (int i = 0; i < N - M; i++) //N - M 数组剩下的元素个数
{
cout << buff[i] << " ";
}
return 0;
}
//逻辑上删除数组中的元素
#include <iostream>
#include <stdio.h>
#include <stdint.h>
using namespace std;
int main()
{
//第一步,输入
int N, M;
cin >> N >> M;
//scanf("%d %d", &N, &M);
int buff[N] = {0};
for (int i = 0; i < N; i++)
{
cin >> buff[i];
//scanf("%d", buff+i);
}
/*单元测试,验证输入的正确性*/
//cout << N << ", " << M << endl;
//for (int i = 0; i < N; i++)
//{
// cout << buff[i] << " ";
//}
for (int i = 0; i < M; i++)
{
//第二步:找到数组中最小元素的下标
int minId = 0;
for (int j = 0; j < N; j++)
{
if (buff[j] == INT_MAX)
continue;
if (buff[minId] > buff[j])
minId = j;
}
// 单元测试2,验证找到的数组中最小的元素和其下标
//cout << buff[minId] << " " << minId << endl;
//第三步:最小元素下标左右都加上最小元素
//找到minId左边未删除的元素
int j;
for (j = minId - 1; j > -1; j--)
{
if (buff[j] != INT_MAX)
break;
}
if (j > -1)
buff[j] += buff[minId];
for (j = minId + 1; j < N; j++)
{
if (buff[j] != INT_MAX)
break;
}
if (j < N)
buff[j] += buff[minId];
//第四步:删除最小元素(逻辑上上删除)
buff[minId] = INT_MAX; //INT_MAX表示元素已经删除
}
//第五步:输出数组剩下的元素
for (int i = 0; i < N; i++) //N - M 数组剩下的元素个数
{
if (buff[i] == INT_MAX)
continue;
cout << buff[i] << " ";
}
return 0;
}
蓝桥杯不能使用<stdint.h>
即不能使用INT_MAX
需要宏定义一个
#include <math.h>
#define MAX pow(2,31)-1 //2的31次-1
算法优化
静态查找,只进行查找操作的查找,用二分法
- 动态查找,在查找过程中同时插入新的数据元素,或者删除已存在的数据元素。用排序树或队列
- 输入不需要优化
- 第二步查找最小值,需要将O(N2)降低为O(NlogN)
- 数据是无序的
- 属于动态查找
- 用排序树,AVL,红黑树
- STL里,set或multiset
- set不允许有重复的键
- multiset允许有重复的键
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <set>
using namespace std;
#define MAX pow(2,31)-1
int main()
{
multiset<int> buff;
buff.insert(5);
for (multiset<int>::iterator it = buff.begin(); it != buff.end(); it++)
{
cout << *it << endl;
}
return 0;
}
代码优化
- 数据存储->DS
- 复杂
- 空间
- 程序设计->算法
- 简单
- 时间
//模拟链表+排序树
//链表,物理上删除元素
//模拟链表,逻辑上删除元素
//逻辑上删除数组中的元素
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#include <vector>
#include <set>
using namespace std;
typedef long long INT64; //数据规模的原因
typedef pair<int, int> Key; //存放值和下标的类型
typedef struct
{
int id; //下标
INT64 val; //值
int prev; //上一个元素的下标
int next; //下一个元素的下标
}Node; //buff模拟链表使用
int main()
{
//第一步,输入
int N, M;
cin >> N >> M;
//scanf("%d %d", &N, &M);
Node buff[N] = {0};
multiset<Key> mSet;
for (int i = 0; i < N; i++)
{
INT64 v;
cin >> v;
buff[i] = {i, v, i-1, i+1}
mSet.insert(Key(v, i));
}
//for (multiset<Key>::iterator it = mSet.begin;
// it != mSet.end(); i++)
//{
// cout << it->first << ", " << it->second << endl;
//}
for (int i = 0; i < M; i++)
{
//第二步:找到数组中最小元素的下标
multiset<Key>::iterator minIt = mSet.begin();
int minId = minIt->second;
int minV = minIt->first;
// 单元测试2,验证找到的数组中最小的元素和其下标
//cout << buff[minId] << " " << minId << endl;
//第三步:最小元素下标左右都加上最小元素
//找到minId左边未删除的元素
int prev = buff[minId].prev;
int next = buff[minId].next;
if (prev != -1)
{
mSet.erase(Key(buff[prev].val, prev));
//删除原有的左值
buff[prev].val += buff[minId].val;
buff[prev].next = buff[minId].next;
mSet.insert(Key(buff[prev].val, prev));
//添加新的左值
}
if (next != N)
{
mSet.erase(Key(buff[next].val, next));
//删除原有的右值
buff[next].val += buff[minId].val;
buff[prev].prev = buff[minId].prev;
mSet.insert(Key(buff[next].val, next));
//添加新的右值
}
//第四步:删除最小元素(逻辑上上删除)
buff[minId].val = MAX; //MAX表示元素已经删除
mSet.erase(minIt);
}
//第五步:输出数组剩下的元素
for (int i = 0; i < N; i++) //N - M 数组剩下的元素个数
{
if (buff[i].val == MAX)
continue;
cout << buff[i] << " ";
}
return 0;
}
代码优化2
优先级队列
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <queue>
using namespace std;
int main()
{
priority_queue<int, vector<int>, greater<int> > qBuff;
qBuff.push(5);
qBuff.push(3);
qBuff.push(2);
qBuff.push(8);
qBuff.push(7);
while(qBuff.empty() == false)
{
cout << qBuff.top() << " ";
qBuff.pop();
}
return 0;
}
示范
/**
* H题:方式五 - 模拟链表+优先队列
* 该程序通过模拟链表和使用优先队列的方式解决问题。
*/
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long INT64;
typedef pair<INT64, int> Key; // Key 定义
typedef struct
{
int prev;
int next;
INT64 sum; //相加之后所放的位置
} Node; // 结点定义
int main()
{
int N, M;
cin >> N >> M; // 输入总数 N 和操作次数 M
// 初始化结点数组和优先队列
Node buff[N] = {0};
/* 核心代码*/
priority_queue, greater> buffQ;
// 初始化链表结点和优先队列
for(int i = 0; i < N; i++)
{
INT64 v;
cin >> v;
buff[i] = {i-1, i+1};
//初始化链表结点,每个结点有前驱和后继
buffQ.push(Key(v, i)); // 将每个结点的值和索引加入优先队列
}
// 执行优化操作,保留前 N-M 个元素
while(buffQ.size() > N-M)
{
Key e = buffQ.top();
buffQ.pop();
int id = e.second; // 获取当前操作结点的索引
INT64 val = e.first; // 获取当前操作结点的值
if(buff[id].sum)
{
/* 核心代码*/
buffQ.push(Key(buff[id].sum + val, id));
// 将当前结点和其后继结点的和重新加入优先队列
buff[id].sum = 0; // 将当前结点的 sum 置零
}
else
{
int prev = buff[id].prev;
int next = buff[id].next;
// 将当前结点的值分配给前驱和后继结点
if(prev != -1)
{
buff[prev].sum += val;
buff[prev].next = buff[id].next;
}
if(next != N)
{
buff[next].sum += val;
buff[next].prev = buff[id].prev;
}
}
}
vector res(N, 0);
// 将优先队列中的结果保存到数组中
while(buffQ.empty() == false)
{
Key e = buffQ.top();
buffQ.pop();
res[e.second] = e.first + buff[e.second].sum;
}
// 输出结果
for(int i = 0; i < N; i++)
{
if(res[i]) cout << res[i] << " ";
}
return 0;
}