第一篇:基础题
四、函数递归题
(15)P4994 终于结束的起点
题目概括:
#include<bits/stdc++.h>
using namespace std;
int main(){
int m;
cin >> m;
int a = 0, b = 1, c;
int i = 0;
while(true){
c = (a+b)%m;
a = b;
b = c;
i++;
if(a==0&&b==1)break ;
}
cout << i;
return 0;
}
/*
#include<bits/stdc++.h>
using namespace std;
int f[10000001];//尽可能大
int main() {
int m;
cin >> m;
f[0] = 0;
f[1] = 1;
for (int i = 2; i < 10000001; i++) {
f[i] = (f[i - 1] + f[i - 2]) % m;
}
int res = 0;
for (int i = 1; i < 10000000; i++) {
if (f[i] == 0 && f[i + 1] == 1) {
res = i;
break;
}
}
cout << res;
return 0;
}
*/
(16)P1025 [NOIP 2001 提高组] 数的划分
题目概括
给定整数 \( n \)(\( 6 < n \leq 200 \))和 \( k \)(\( 2 \leq k \leq 6 \)),求将 \( n \) 分成 \( k \) 个不为空且不考虑顺序的不同分法的数量。
DFS+剪枝(减少重复)
// DFS+剪枝(减少重复)
#include<bits/stdc++.h>
using namespace std;
//三个变量:num(剩余待分的整数),part(剩余分的份数,初始为k),start(当前要选择的数的最小值,初始为1) 注:每次选择一个数后,下一个数的选择从当前数开始,以避免重复分法
int dfs(int num,int part,int start){
//首先特殊情况
if(part==1) return 1;
int sum = 0;
// 当前选择的数不能超过num/part,是一种剪枝操作,比如(6,2,1)时i<=3,i会有1、2、3中情况,但i=4时,剩余1份分配为2,这与前面的i=2,剩4重复了
for(int i =start; i<= num/part;i++){
sum += dfs(num-i,part-1,i);
}
return sum;
}
int main(){
int n,k;
cin >> n >>k;
cout <<dfs(n,k,1)<<endl;
return 0;
}
动态规划(递推)
//动态规划(递推)
//dp[i][j] :将i分成j份的种类数
//因为6<n,k>=6,所以每份至少为1
#include<bits/stdc++.h>
using namespace std;
int dp[201][6];
int main(){
int n,k;
cin >> n>>k;
//初始化
for(int i=1;i<=n;i++){
dp[i][1] = 1;
}
for(int i =2;i<=n;i++){
for(int j = 2;j<=k;j++)
//dp[i-1][j-1] 表示组合中含1(当前),dp[i-j][j] 表示不含1,理解为先为j都分配1,故等价于i-j再分j份(每份>1) " if(i>=j)"一定要写
if(i>=j) dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
}
cout <<dp[n][k];
return 0;
}
(17)P1192 台阶问题
有 N 级台阶,你一开始在底部,每次可以向上迈 1∼K 级台阶,问到达第 N 级台阶有多少种不同方式。
//斐波那契数列思想,这道题的基础版:https://zhaotong.fun/2022/06/15/dynamic-programing.html
// class Solution {
// public:
// int climbStairs(int n) {
// vector<int>dp(n+1);
// dp[1]=1;
// dp[0]=1;
// for(int i =2;i<=n;i++){
// dp[i] = dp[i-1]+dp[i-2];
// }
// return dp[n];
// }
// };
#include<bits/stdc++.h>
using namespace std;
const int mod = 100003;
int n,k,dp[100001];
int main(){
cin >> n>>k;
dp[0] = dp[1]=1;
for(int i =2;i<=n;i++){
for(int j=1;j<=k;j++){
if(i>=j){
dp[i] = (dp[i]+dp[i-j])%mod; //dp[i-k]从第 i−k级台阶迈k级到达第 i 级台阶的所有可能方式,累加形式(不除模的话可以写成dp[i]+=dp[i-j])
}
}
}
cout << dp[n]%mod;
return 0;
}
(18)P1464 Function
实现递归函数 \(w(a,b,c)\),按给定条件(如 \(a,b,c \leq0\) 或 \(>20\) 等不同情况的返回规则)计算结果,需优化以应对重复调用问题,多组输入(以 \(-1,-1,-1\) 结束),输出格式为 \(w(a, b, c) = ans\)。
//记忆化减少重复
#include<bits/stdc++.h>
using namespace std;
//定义数组储存计算过的数,21是因为>20的返回w(20,20,20)
int w[21][21][21]; //已经计算过的 w(a, b, c) 的值
bool v[21][21][21];//标记 w(a, b, c) 是否已经被计算过,true则返回w[a][b][c]
//w[n][m][z],v[n][m][z]初始为 0和false
int f(long long a,long long b,long long c){ //f计算w(a,b,c)的值
if(a<=0 or b<=0 or c<=0) return 1;
if(a>20 or b>20 or c>20) return f(20,20,20);
if(v[a][b][c]) return w[a][b][c]; //计算过则直接取值
if(a<b & b<c){
w[a][b][c] = f(a,b,c-1)+f(a,b-1,c-1)-f(a,b-1,c);
}else{
w[a][b][c] = f(a-1,b,c)+f(a-1,b-1,c)+f(a-1,b,c-1)-f(a-1,b-1,c-1);
}
v[a][b][c] = true; //标记
return w[a][b][c];
}
int main(){
long long a,b,c;
// scanf("%lld %lld %lld",&a,&b,&c); //为了判断第一次a,b,c能否进入循环
// while(a!=-1 or b!=-1 or c!=-1){
// printf("w(%lld, %lld, %lld) = %d\n",a,b,c,f(a,b,c));
// scanf("%lld %lld %lld",&a,&b,&c);
// }
while(1){
cin >>a>>b>>c;
if(a==-1 & b==-1&c==-1) return 0;
cout << "w("<<a<<", "<<b<<", "<<c<<")"<<" = "<<f(a,b,c) <<endl;
}
}
(19)P5534 【XR-3】等差数列
给定等差数列的前两项 \(a_1\)、\(a_2\) 以及项数 n,求该等差数列的各项之和。
//直接上求和公式
#include<bits/stdc++.h>
using namespace std;
int main(){
long a,b,n,c;
cin >> a>>b>>n;
c= b-a;
cout << a*n +n*(n-1)*c/2;
}
给定 \( n \) 个整数和整数 \( k \)(\( k < n \)),计算从这 \( n \) 个数中选 \( k \) 个相加,和为素数的组合种数。
//组合题+素数,全排列题目见P1706
#include<bits/stdc++.h>
using namespace std;
int m,k,res=0;
int nums[21];
//判素数
bool isprime(int n){
if(n<2) return false;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0) return false;
}
return true;
}
//DFS s为搜索起始位置,cut为已选取的个数 sum为选取数的和
void dfs(int s,int cut,int sum){
if(cut == k){
if(isprime(sum)){
res ++;
}
}
for(int i=s;i<m;i++){
dfs(i+1,cut+1,sum+nums[i]);
}
}
//主函数
int main(){
cin >> m>> k;
for(int i=0;i<m;i++){
cin >> nums[i];
}
dfs(0,0,0);
cout << res<<endl;
return 0;
}
(21)P1028 [NOIP 2001 普及组] 数的计算
给定正整数 \( n \),构造合法数列:单个 \( n \) 是合法数列;合法数列末尾可加不超过最后一项一半的正整数生成新合法数列。求合法数列的总个数。
//动态规划思想
#include<bits/stdc++.h>
using namespace std;
int nums[1001];
//核心:理解nums[i]的含义->以i为结尾的合法数列的总数(注意是总数)
int main(){
int n;
cin >> n;
// 对于每个 i,尝试所有可能的 j(可以添加到 i 后面的数字)
for(int i=1;i<=n;i++){
//j从1开始(题目要求正整数)
for(int j=1;j<=i/2;j++){
nums[i] += nums[j]; // 将 j 的合法数列数量累加到 i 的总数中
}
nums[i] ++; // i自身单独成数列的情况
}
cout << nums[n];
return 0;
}
五、模拟题
已知 n 张矩形地毯按编号从小到大依次铺在平面直角坐标系第一象限,后铺的覆盖先铺的。给出每张地毯的左下角坐标及 x、y 方向长度,以及一个点的坐标,求覆盖该点的最上面那张地毯的编号,无覆盖则输出 \(-1\)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int a[maxn],b[maxn],c[maxn],d[maxn];
int main(){
int n;
cin >> n;
for(int i=0;i<n;i++){
cin >> a[i] >>b[i]>>c[i]>>d[i];
}
int x,y,res=-1;
cin >>x>>y;
for(int i =0;i<n;i++){
if(x>=a[i]&&y>=b[i]&&x<=a[i]+c[i]&&y<=b[i]+d[i]){
res = i+1; //层数加1
}
}
cout << res;
return 0;
}
(23)P1328 [NOIP 2014 提高组] 生活大爆炸版石头剪刀布
两人玩石头剪刀布升级版游戏(含剪刀、石头、布、蜥蜴人、斯波克五种手势,有特定胜负关系),出拳有周期性。给定共 N 次猜拳,以及小 A、小 B 出拳的周期长度和周期内出拳规律,统计 N 次猜拳后两人得分。
#include<bits/stdc++.h>
using namespace std;
int vs[5][5] = { // 胜负关系表(核心规则)
{0,0,1,1,0}, // 剪刀:赢布、蜥蜴人
{1,0,0,1,0}, // 石头:赢剪刀、蜥蜴人
{0,1,0,0,1}, // 布:赢石头、斯波克
{0,0,1,0,1}, // 蜥蜴人:赢布、斯波克
{1,1,0,0,0} // 斯波克:赢石头、剪刀
};
int n,na,nb;// 总猜拳次数、小A周期、小B周期
int x=0,y=0;// 记录得分
int main(){
cin>> n>>na>>nb;
int a[na],b[nb];
for(int i=0;i<na;i++) cin >> a[i];
for(int i=0;i<nb;i++) cin >>b[i];
for(int i =0;i<n;i++){
int pa = a[i%na]; // 小A出拳
int pb = b[i%nb]; // 小B出拳
x += vs[pa][pb];
y += vs[pb][pa];
}
cout << x<<" "<<y;
return 0;
}
//笨方法 纯模拟
// #include<bits/stdc++.h>
// using namespace std;
// int f(int a,int b ){
// if (a == b) return 0;
// if (a == 0 && b == 1) return 1;
// if (a == 0 && b == 2) return 2;
// if (a == 0 && b == 3) return 2;
// if (a == 0 && b == 4) return 1;
// if (a == 1 && b == 0) return 2;
// if (a == 1 && b == 2) return 1;
// if (a == 1 && b == 3) return 2;
// if (a == 1 && b == 4) return 1;
// if (a == 2 && b == 0) return 1;
// if (a == 2 && b == 1) return 2;
// if (a == 2 && b == 3) return 1;
// if (a == 2 && b == 4) return 2;
// if (a == 3 && b == 0) return 1;
// if (a == 3 && b == 1) return 1;
// if (a == 3 && b == 2) return 2;
// if (a == 3 && b == 4) return 2;
// if (a == 4 && b == 0) return 2;
// if (a == 4 && b == 1) return 2;
// if (a == 4 && b == 2) return 1;
// if (a == 4 && b == 3) return 1;
// }
// //周期通过%来循环
// int main(){
// int n,na,nb;
// cin>>n>>na>>nb;
// int a[201],b[201];
// int x =0,y= 0; //计数
// for(int i=1;i<na;i++) cin>>a[i];
// for(int i=1;i<na;i++) cin >>b[i];
// for(int i=1;i<n;i++){
// int pa = i%na,pb = i%nb;////pa,pb= na,b%T(周期) 循环
// if(pa ==0 ) pa = na;
// if(pb == 0) pb = nb; //边界情况
// int p = f(a[pa],b[pb]);
// if(p!=0){
// if(p==2) x++;
// else if (p==1) y++;
// }
// }
// cout << x<<" "<<y;
// return 0;
// }
(24)P1067 [NOIP 2009 普及组] 多项式输出
给定一元 \( n \) 次多项式的次数 \( n \) 及各项系数,按次数递减、只含非零系数等规则,格式化输出该多项式。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, a; cin >> n;
for(int i=n; i>=0; i--){ //指数递减给出多项式,故倒序循环
cin >> a;
if(a){
if(i<n&&a>0) cout << '+';
if(abs(a)>1||i==0) cout << a;
if(a==-1&&i) cout << '-'; //a=-1特殊情况
if(i>0) cout << 'x';
if(i>1) cout << '^' << i;
}
}
return 0;
}
(25)P1563 [NOIP 2016 提高组] 玩具谜题
n 个玩具小人围成圈,各有朝向(朝内 / 朝外)和职业。从第 1 个小人开始,按 m 条 “向左 / 右数 s 个小人” 的指令移动,求最终到达小人的职业。
结构体:当对象有多个相关联的属性,结构体可以方便地将这两个属性绑定在一起,便于统一管理和操作
//结构体:当对象有多个相关联的属性,结构体可以方便地将这两个属性绑定在一起,便于统一管理和操作
#include<bits/stdc++.h>
using namespace std;
//定义结构体
struct node
{
bool f; //表示玩具小人的朝向
string name; //职业
};
node nums[100001];
int main(){ // 修正了拼写错误
int n,m,a,s;
cin >>n>>m;
for(int i = 0;i<n;i++) cin>> nums[i].f >> nums[i].name;
int st = 0;
for(int i =0;i<m;i++){
cin >> a>>s;
if(nums[st].f^ a)//异或运算 (逆时针)
st = (st+s)%n;// 计算新的位置,使用模运算确保在环内
else
st = (st-s+n)%n;// 计算新的位置,加n后再取模避免负数
}
cout<< nums[st].name;
return 0;
}
不使用结构体
//不使用结构体
#include <iostream>
#include <string>
using namespace std;
int main() {
int n, m, a, s;
cin >> n >> m;
// 声明两个数组分别存储朝向和职业
bool flag[100001]; // 存储每个小人的朝向
string name[100001]; // 存储每个小人的职业
for (int i = 0; i < n; i++) {
cin >> flag[i] >> name[i];
}
int start = 0;
for (int i = 0; i < m; i++) {
cin >> a >> s;
if (flag[start] ^ a)
start = (start + s) % n;
else
start = (start - s + n) % n;
}
cout << name[start];
return 0;
}
(26)P1179 [NOIP 2010 普及组] 数字统计
统计给定范围 \([L, R]\) 内所有整数中数字 2 出现的总次数。
#include<bits/stdc++.h>
using namespace std;
int main(){
int l,r,res = 0;
cin >> l>>r;
for(int i = l;i<=r;i++){
int tmp= i;
while(tmp>1){
if(tmp%10 ==2) res ++;
tmp/=10;
}
}
cout << res;
return 0;
}
(27)P2615 [NOIP 2015 提高组] 神奇的幻方
给定奇数 \( N \),按规则构造 \( N×N \) 幻方:1 放第一行中间,后续数 \( K \) 按“优先放右上,右上有数则放下”等规则填充,使每行、每列及对角线数字和相同。
#include <iostream>
using namespace std;
int main() {
int n;
int k = 1, x, y; //k表示第几个数,x,y表示k所在的位置
int a[45][45] = {};
cin >> n;
for (k = 1; k <= n * n; k++) { //使用for循环来控制k的值
if (k == 1) { //特判,最开始时
x = 1;
y = n / 2 + 1; //一行的中间位置
} else if (x == 1 && y != n) {
x = n;
y++;
} else if (y == n && x != 1) {
x--;
y = 1;
} else if (x == 1 && y == n) {
x++;
} else {
if (a[x-1][y+1]) {
x++;
} else {
x--;
y++;
}
}
a[x][y] = k; //填写k
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << a[i][j] << " ";
}
cout << "\n";
}
return 0;
}
----2025.9.3
写到这里主包也是有点感慨,今天主包拿到了整个保研过程中的第一个offer,此前也是屡屡碰壁,甚至有的211的夏令营都进不去(入营的不是宣讲就是纯实践营,前后准备了很久的某九的夏令营面试也喜提低位候补(T_T)),一度让我开始怀疑自己的能力。现在开始主包的刷题不在单纯针对保研,也是为了大四投递实习,一起加油吧!