目录
problem 2:peter的烟(洛P1150)【蓝桥:饮料换购】
problem 1: 质因数分解(洛P1075)
题目描述
已知正整数 n 是两个不同的质数的乘积,试求出两者中较大的那个质数。
输入格式
输入一个正整数 n。
输出格式
输出一个正整数 p,即较大的那个质数。
输入输出样例
输入 #1 21
输出 #1 7
分析:
目标:熟悉基础的数论代码,分解质因数、求约数
做题思路:
利用了约数成对存在的性质,我们只需要枚举出最小的质约数,就可以很好的解决问题
代码:
//这题利用了约数成对出现的规律,只要找到一个最小的质约数就可以了
#include<iostream>
#include<cmath>
using namespace std;
bool prime(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0)return false;
}
return true;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0 &&prime(i)){
printf("%d",n/i);
break;
}
}
return 0;
}
problem 2:peter的烟(洛P1150)【蓝桥:饮料换购】
分析:
目标:熟悉模拟题、迭代法。其实没有考察什么知识点,快速过吧
做题思路:res记录答案,只要我们的n(烟蒂)比兑换阈值(k)要大,我们就可以继续换烟,在此过程n不断减少,res不断叠加
代码:
#include <iostream>
using namespace std;
int main()
{
// 迭代法
int n,k,res;
cin>>n>>k;
res=n;
while(n>=k)
{
res+=n/k; //res可以兑换的部分
n=n/k+n%k; //记住每兑换1个烟,会再次得到一个烟蒂
}
cout<<res<<endl;
return 0;
}
problem 3: 无独有偶(牛:训练重现赛A)
分析:
目标:学会细心处理问题,不要陷入为主!
做题思路:
求最小值,那我们只需要对大于0的偶数进行处理即可
1.判断奇数偶数
2.将符合条件的偶数处理到不能被2整除的最小数
代码:
#include<iostream>
typedef long long LL;
using namespace std;
int func(int x)
{
while(x%2==0)
x/=2;
return x;
}
int main()
{
LL n,ans=0;
cin>>n;
while(n--)
{
int x;
cin>>x;
if(x>0){
if(x%2==0)ans+=func(x);
else ans+=x;
}
else ans+=x;
}
cout<<ans;
return 0;
}
problem 4: 与众不同(牛:训练重现赛B)
分析:
目标:熟悉相关位运算的知识,越与越小的性质
做题思路:
1.如果不知道与运算的性质的同学,可以使用动态规划来解决(数据范围也很明显提示可以使用动态规划)
2.知道的同学就直接遍历与一遍就是答案
代码就展示动态规划的写法吧,另一个没啥好写的
代码:
#include<stdio.h>
#define min(x,y) (x)<(y)?(x):(y)
const int N = 25;
long long f[N],a[N];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[1]=a[1];
for(int i=2;i<=n;i++)
f[i] = min(f[i-1],f[i-1]&a[i]);
printf("%d",f[n]);
return 0;
}
知识拓展:位运算知识点
1. 按位与(&):将两个整数按位进行“与”操作,只有当对应位都为1时才得到1,否则得到0。例如,3 & 5 = 1,因为3的二进制表示为011,5的二进制表示为101,在第1和第3位上都是1,所以结果为1。
2. 按位或(|):将两个整数按位进行“或”操作,只要对应位有一个为1就得到1,否则得到0。例如,3 | 5 = 7,因为3的二进制表示为011,5的二进制表示为101,在第1、第2和第3位上至少有一个1,所以结果为111=7。
3. 按位异或(^):将两个整数按位进行“异或”操作,只有当对应位不同时才得到1,否则得到0。例如,3 ^ 5 = 6,因为3的二进制表示为011,5的二进制表示为101,在第1和第3位上不同,所以结果为110=6。
4. 左移位(<<):将一个整数向左移动指定的位数,相当于乘以2的移动位数次方。例如,3 << 2 = 12,因为3向左移动2位等于3×2²=12。
5. 右移位(>>):将一个整数向右移动指定的位数,相当于除以2的移动位数次方取整。例如,3 >> 1 = 1,因为3向右移动1位等于1(余1),即3÷2=1(余1)。
problem 5:寻寻觅觅(牛:训练重现赛C)
分析:
目标:深入认识模拟、搜索,掌握dfs、bfs的模板书写
题目思路:
实际上就是简单得不能再简单的图的搜索而已,注意一下边界条件、标记数组即可。写的时候多加以画图辅助,轻而易举解决。
代码1:DFS搜索
#include<iostream>
const int N = 110;
using namespace std;
typedef long long LL;
int mp[N][N];
bool vis[N][N];
int sx,sy,n,m;
LL ans;
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
void dfs(int x,int y)
{
vis[x][y]=true;
int tx,ty;
for(int i=0;i<=3;i++)
{
tx = x+dx[i];
ty = y+dy[i];
if(tx<1||tx>n||ty<1||ty>m)continue;
if(mp[tx][ty]==-1)continue;
if(vis[tx][ty])continue;
vis[tx][ty]=true;
ans+=mp[tx][ty];
dfs(tx,ty);
}
return ;
}
int main()
{
cin>>n>>m>>sx>>sy;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
ans+=mp[sx][sy];
vis[sx][sy]=true;
dfs(sx,sy);
cout<<ans;
return 0;
}
problem 6:二泉映月(牛:训练赛重现赛D)
分析:
目标:掌握哈希表,意识到c++的stl的好用
做题思想:
1.哈希映射函数:int hash(int x){ }
返回对应数字应该放置的位置
2.哈希添加数字函数:void add(int x){ }
首先使用哈希函数计算数字的位置,然后遍历该位置的链表,查找数字是否已经在哈希表中。如果数字已经存在,则将其计数器加1。如果数字不存在,则创建一个新的节点,将其插入到链表的头部,并将其计数器初始化为1。
3.哈希数字查询函数:int query(int x){ }
首先将该数字通过哈希函数转换为哈希表中的位置,然后在该位置的链表中查找该数字的节点。如果找到了该节点,我们返回其计数器;否则,我们返回0。
代码1:(c语言)
#include <stdio.h>
#include <stdlib.h>
#define MAXN 1000000
#define MAXX 1000000000
//1.定义一个哈希表结构体 包括数字、次数以及链表
typedef struct node {
int value;
int count;
struct node *next;
} Node;
//2。定义哈希表
Node *hash_table[MAXN];
//3.哈希函数(映射数字)
int hash(int x) {
return x % MAXN;
}
//4.哈希数字添加
void add(int x) {
int h = hash(x); //4.1找到合适的插入位置
Node *current = hash_table[h]; //4.2定义一个指针,指向这个初始的位置
while (current != NULL) { //4.3只要这个位置不是空,我们就往下找,直到找到值为x
if (current->value == x) {
current->count++; //4.4成功找到x,让它的次数加一
return;
}
current = current->next;
}
//4.5找不到x,创建一个新节点,存储x
Node *new_node = (Node *) malloc(sizeof(Node));
new_node->value = x;
new_node->count = 1;
new_node->next = hash_table[h];
hash_table[h] = new_node;
}
//5.哈希次数查询
int query(int x) {
int h = hash(x); //5.1找到合适的初始插入位置
Node *current = hash_table[h]; //5.2让指针指向这个初始位置
while (current != NULL) { //5.3指针不为空就一直找
if (current->value == x) { //5.4成功找到,返回次数
return current->count;
}
current = current->next; //否则,返回0
}
return 0;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int p, x;
scanf("%d %d", &p, &x);
if (p == 1) {
add(x);
} else {
int count = query(x);
printf("%d\n", count);
}
}
return 0;
}
代码2:(领悟c++的魅力)
#include <unordered_map>
#include <iostream>
using namespace std;
class MyDataStructure {
private:
unordered_map<int, int> counts;
public:
void add(int x) {
counts[x]++;
}
int query(int x) {
return counts[x];
}
};
int main() {
MyDataStructure data_structure;
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int p, x;
cin >> p >> x;
if (p == 1) {
data_structure.add(x);
} else {
int count = data_structure.query(x);
cout << count << endl;
}
}
return 0;
}
知识拓展:C++STL 哈希表
problem 6: 一箭双雕(牛:训练赛重现赛E)
分析:
目标:掌握结构体的使用、pair的使用、重载比较函数、sort或qsort的使用、区间合并思想
题目思路:
1.将左右区间进行排序,按照右区间进行排序
2.排序万后,进行
problem 7:博弈犹贤(牛:训练赛重现赛H)
分析:
目标:掌握博弈论的分析技巧,动态规划区间DP的思想及代码的实现
题目思想:
1.清楚博弈论讲了什么:
确定以下几个要素:
博弈参与者:在这个问题中,参与者是 Jack 和 Rose。
博弈规则:在这个问题中,参与者轮流操作,每次操作可以拿走最左边或者最右边的一张纸牌。当桌面上的纸牌被拿完时,各自统计纸牌的点数之和。
博弈目标:在这个问题中,目标是使自己的点数尽可能大。
2.分析实现的方式:
考虑使用动态规划来解决这个问题。我们定义 dp[i][j] 表示从第 i张纸牌到第 j 张纸牌中,当前玩家可以获得的最大点数。对于当前玩家来说,他可以选择拿走左边的一张纸牌或者右边的一张纸牌。那么可以找到状态转移方程:
我们再利用y总三步法分析一波:
其中,sum[i][j] 表示第 i张纸牌到第 j 张纸牌的点数之和。根据上述转移方程,我们可以从小区间向大区间递推,最终得到 dp[1][n],即 Jack 可以获得的最大点数。
jack可以获得的点数:dp【1】【n】;
rose可以获得的点数:sum【1】【n】-dp【1】【n】
时间复杂度为 O(n^2)。
最后,再来一波y总分析法:
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010;
int n;
int a[N]; // 存储纸牌点数的数组
int dp[N][N]; // dp数组,用于记录当前区间内当前玩家可以获得的最大点数
int sum[N][N]; // sum数组,用于记录每个区间内所有纸牌点数的和
int main() {
cin >> n; // 输入纸牌的数量
for (int i = 1; i <= n; i++) {
cin >> a[i]; // 输入每个纸牌的点数
sum[i][i] = a[i]; // 初始化sum数组
dp[i][i] = a[i]; // 初始化dp数组
}
// 从小区间向大区间递推dp数组
for (int len = 2; len <= n; len++) { // 枚举区间长度
for (int i = 1; i + len - 1 <= n; i++) { // 枚举区间左端点
int j = i + len - 1; // 区间右端点
sum[i][j] = sum[i][j-1] + a[j]; // 更新sum数组
dp[i][j] = max(sum[i][j] - dp[i+1][j], sum[i][j] - dp[i][j-1]); // 更新dp数组
}
}
int jack = dp[1][n]; // Jack可以获得的最大点数
int rose = sum[1][n] - jack; // Rose可以获得的点数
if (jack > rose) cout << "win" << endl; // 输出结果
else if (jack == rose) cout << "draw" << endl;
else cout << "lose" << endl;
return 0;
}