写在前面:这篇博客主要是自己复习专业课来用的,由于有一些程序设计题,我觉得还是以博客的形式把代码和个别思路写下来会好一些。由于这些代码并没有提交AC,所以出错概率很大(毕竟以前比赛自己一次AC的经历也不是很多),如果发现哪里有问题,欢迎在评论区留言讨论。
==========================================================
总结:
编程题部分的博客已经全部结束,可以看出来所有的编程题,基本没有思维难度,前两题一般都是暴力模拟,最后一题出过背包方案数,背包具体方案,贪心区间,树状数组求逆序对之类的经典问题,考察相应算法的实现。所以平时注意一些经典算法的复习即可。此外还有诸如dfs暴力,链表操作等基本功,最后一道编程题也有过组合数dp这种考场上短时间未必能快速想出来的情况,但是最后一题只有10分,所以直接暴力拿部分分影响其实也不是很大。
最后,编程题总体不难,后期要重点放在数据结构基础知识和理论这一块,注意各题型时间的分配,平时一定要练习手写代码。
==========================================================
【2014~2022历年题目】
一.西交915 2014研究生入学考试 编程题
二.西交915 2015研究生入学考试 编程题
三.西交915 2016研究生入学考试 编程题
四.西交915 2017研究生入学考试 编程题
五.西交915 2018研究生入学考试 编程题
六.西交915 2019研究生入学考试 编程题
七.西交915 2020研究生入学考试 编程题
八.西交915 2021研究生入学考试 编程题
九.西交915 2022研究生入学考试 编程题
十.西交915 2023研究生入学考试 编程题
一.西交915 2014研究生入学考试 编程题
1.进制转换:输入十进制,分别输出相应的二,八和十六进制
思路:基本操作,但还是记录一下,对于十进制数X转N进制,
X%N得到是N进制的最后一位
X=X/N相当于将N进制最后一位去掉
这样我们每次k=X%N 再X=X/N就可以获得N进制的每一位
代码如下:
#include
using namespace std;
string Get(int x,int n){
string res;
while(x){
int k=x%n;
x/=n;
if(k>10) res+='A'+k-10;
else res+=to_string(k);
}
reverse(res.begin(),res.end());
return res;
}
int main(){
int x;
cin>>x;
cout<<Get(x,2)<<" "<<Get(x,8)<<" "<<Get(x,16)<<endl;
}
2.输入字符串,以回车结束,判断其是否为回文串。
代码:
#include
using namespace std;
const int N=1e6+5;
char ch[N];
int cnt=0;
bool Judge(char ch[]){
int cnt=strlen(ch);
for(int i=0,j=cnt-1;i<=j;i++,j--){
if(ch[i]!=ch[j]) return false;
}
return true;
}
int main(){
//读入回文串
gets(ch);
//判断 是回文串输出YES,否者输出NO
if(Judge(ch))cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
3.结构体排序。em…由于题面太长这里就不贴题面了。PS:这里提以前前几年的题都比较简单,我买的真题BD都是用C语言写的,但是最近的题目越来越难,给出的答案都是C++,也就是说我们是可以直接使用STL的。(应该?)
代码:
#include
using namespace std;
const int N=1e6+5;
struct Book{
string name;//书名
int num_save;//库存量
int num_sale;//销售量
double price;//单价
};
Book books[31];
bool cmp(Book book1,Book book2){
return book1.num_sale>book2.num_sale;
}
int main(){
//输入书籍信息;
for(int i=0;i<30;i++)cin>>books[i].name>>books[i].num_sale>>books[i].num_save>>books[i].price;
for(int i=1;i<=100;i++){
string name;
cin>>name;
for(int j=0;j<30;j++){
if(books[i].name==name){
books[i].num_sale++;
books[i].num_save--;
}
}
}
sort(books,books+30,cmp);
for(int i=0;i<30;i++)cout<<books[i].name<<" "<<books[i].num_sale<<" "<<books[i].num_save<<endl;
}
二.西交915 2015研究生入学考试 编程题
(前几年过于简单的题这里就不写了,感觉没那个必要…)
1.编写函数:分别输出一个矩阵的主对角线元素和和副对角线元素和。(略)
2.编写函数,分别统计各字符串字符出现次数。(略)
3.输入m次字符串,输出出现过的字符串出现次数。(略)
4.链表题,写一个函数输出链表中节点值为x的个数。
虽然这本题也过于简单 ,但是鉴于这题说明了西交曾经考过链表题,而有些链表题也可以出的很复杂,所以这里贴一下代码。
平日复习注意链表题的练习,很多基本操作如反转链表,循环链表,合并链表等代码实现要熟练掌握
代码:
int caortx(structLNode *HL,int x){
int res=0;
for(auto t=HL;t;t=t->next){
if(t->val==x)res++;
}
return res;
}
三.西交915 2016研究生入学考试 编程题
1.编写函数,将字符串s中的所有数字字符去掉,保留其余字符,并将新的字符串存储再原来s的空间中。
本题其实暗含了空间限制,所以考虑纯C语言,然后用双指针写。
void deleteNumber(char s[]){
int len=strlen(s);
for(int i=0,j=0;i<len;i++){
if(s[i]>='0'&&s[i]<='9'){
s[i]='\0';
continue;
}
s[j]=s[i],j++;
}
}
2.编写函数,统计一个整数里面有多少位等于数字n(n若不是个位数直接返回-1)(略)
3.结构体,类似2014,写出数据结构,写出算法描述(其实啥算法都没有,直接模拟),写出源程序。(略,但是多提一句,这题看答案解析上写的竟然是整张试卷的压轴题,并说因为这道题所以没人满分…这题虽然没有任何的思维难度,但是考场上都是在纸上手写代码,本题的代码量相对较大,这也是答案给出的本题难点所在,在备考过程中,尤其临近考试一两个月时请务必多多练习手写大量的代码,提高代码手写速度,同时注意代码规范)
四.西交915 2017研究生入学考试 编程题
ps:915的2017年的题目与之前变化相对较大,原本40分的程序设计改为了40的算法设计(有区别,但是个人感觉区别不大,一般是写算法思想,写代码实现),不过18年后17年的40分的算法设计又变回了之前的40分的程序设计。注意,17年开始,915的题目开始变难了。
1.写出折半查找的算法的基本思想和代码实现。
算法基本思想:折半查找其实就是二分查找。在一个有序表中,我们先取出当前有序表中的中间值,如果要查找的值小于该值则在当前区间的前半部分进行折半查找;如果要查找的值大于中间值,则在当前区间的后半部分进行折半查找了;否则就是中间值的位置的元素我们要找的元素,算法结束。
代码实现:
//长度为n的有序表a,有序表中的元素下标从1开始
//折半查找有序表中为值为k的元素的位置
int binarySearch(int a[],int n,int k){
int l=1,r=n;
while(l<r){
int mid=(l+r+1)/2;
if(k>a[mid])l=mid+1;
if(k<a[mid])r=mid-1;
if(k==a[mid]) return mid;
}
return -1;
}
2.链表题,输出单链表倒数第k个节点的值,写出基本思想,实现步骤,代码实现。
基本思想:假设该链表一共有n个节点,则倒数第k个节点即为正数n-k+1个节点,所以从头节点开始遍历到第n-k+1个节点后输入该点的值即可。
实现步骤:
(1)用一个指针变量t从头节点开始遍历,统计节点个数,直到该指针变量指向null。
(2)判断,若k>n,则查找失败返回0。
(3)用一个指针变量从头节点开始遍历,遍历时统计当前已经遍历到哪一个点,遍历到第n-k+1个节点时直接返回该点的data值
代码实现:
//题目中给出的link即next
int find(linklist *list,int k){
int cnt=0;
for(auto t=list;t;t=t->link)cnt++;
if(k>cnt)return 0;
for(auto t=list,i=1;t;t->link,i++){
if(i==n-k+1) return t->data;
}
}
3.两个栈s0和s1共享一片连续的存储区域elem[1~maxsize-1],设计共享栈以及有关出栈和入栈的操作。写出基本思想和代码实现。
基本思想:将elem[0],elem[maxsize-1]定义为s0和s1的栈底(记为top0和top1);若s0入栈elem[++top0]=x,出栈elem[top–]=-1(在合法的情况下),若s1入栈elem[–top1]=x,s1出栈elem[top1++]=-1;
代码实现:
//默认初始所有元素为-1
typedef struct Stack{
int elem[maxsize];
int top[0]:0;
int top[1]:maxsize-1;
}s;
void pop(int id){
int top=s.top[id];
if(id==0){
if(elem[top]!=-1){
elem[top]=-1;
top--;
}
}
if(id==1){
if(elem[top]!=-1){
elem[top]=-1;
top++;
}
}
}
void push(int id,int x){
int top=s.top[id];
if(id==0){
if(top+1<=maxsize-1&&elem[top+1]==-1){
elem[top+1]=x;
top++;
}
}
if(id==1){
if(top-1>=0&&elem[top-1]==-1){
elem[top-1]=x;
top--;
}
}
}
4.贪心区间问题:求最大不相交区间的数量。(em…这题是17年的压轴题,可以看出17年的题已经相比前几年变难了,至少开始出现思维题和除了暴力和模拟以外的算法题了)
基本思想:有关贪心区间的问题,一般要么以左端点排序,要么以右端点排序(比较经典的几个模型都是的)。具体证明一般都是反证法。这里就不在赘述了。
代码实现:
#include
using namespace std;
int n;
struct node{
int l,r;
bool operator<(const node&t){
return r<t.r;
}
};
main(){
cin>>n;
vector<node>q;
for(int i=0;i<n;i++){
int l,r;
cin>>l>>r;
q.push_back({l,r});
}
sort(q.begin(),q.end());
int res=1,ed=q[0].r;
for(int i=0;i<n;i++){
if(ed<=q[i].l){
res++;
ed=q[i].r;
}
}
cout<<res<<endl;
}
PS:
1.注意排序算法的复习:915曾经考察过二分的思想和代码实现,其他排序算法比如快排,归并的算法思想和实现复习时要留意复习,没准还考。
2.注意链表专题的复习:915在15和17年均考察了链表题,虽然17年的链表题依然很简单,但是可以看出来,难度比15年已经有所提高了。
3.注意经典问题的复习:这里主要是是指以前比赛中见过的各种经典问题,比如17年考过的这种贪心区间问题,以前自己也写过一篇总结此类问题的博客ACM五大贪心区间问题总结,另外听上岸的学长说最近几年还考过多权值最短路,DP滚动数组优化这些,坦白的说,虽然这些东西在ACM里面并不算不难,从考研的角度来说还是要认真准备,毕竟915是手写代码并且完全闭卷不能带板子的。
五.西交915 2018研究生入学考试 编程题
1.输入一堆字符串,统计0~9分别有多少个(略)
2.C++自定义排序(略)
3.日期问题:输入一个日期,如20170812,判断该日期是该年的第几天。
蓝桥杯年年出这种日期问题,直接暴力,hh
#include
using namespace std;
int months[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool isLeafYear(int year){
if(year%400==0||(year%4==0&&year%100)) return 1;
return 0;
}
int getDay(int year,int month){
if(month==2) return months[month]+isLeafYear(year);
return months[month];
}
main(){
int year,month,day;
cin>>year>>month>>day;
int cnt=0;
bool flag=0;
for(int m=1;m<=month;m++){
for(int d=1;d<=getDay(year,month);d++){
cnt++;
if(m==month&&d==day){
flag=1;
break;
}
}
if(flag==1)break;
}
cout<<cnt<<endl;
}
4.m个球装到n个盒子中,有多少种装法?(每个盒子可以为空,123和321视为同一种装法)
由于没有给数据范围和时间限制,所以直接暴力dfs即可,考场上时间紧,怎么快怎么写,这不是比赛,没有要求时间复杂度,据说当时这题很多人就是因为时间不够直接空着了,在考场上不必去思考最优解,先写出来再说
代码:(这个代码是我自己写的递归,我只测试简单的部分样例过 和网上的n=m=7 样例,答案是15)
#include
using namespace std;
int n,m;//n个盒子,m个球
int dfs(int u,int cnt,int sum){
if(u==n){
if(sum==n)return 1;
return 0;
}
int res=0;
for(int i=cnt;i>=0;i--){
res+=dfs(u+1,i,sum+i);
}
return res;
}
main(){
cin>>n>>m;
int res=0;
for(int i=0;i<=m;i++){
res+=dfs(1,i,i);//第一个盒子放i个球 ,且当前装了i个球
}
cout<<res<<endl;
}
ps:注意手写代码的练习还有考场时间问题,18年的试卷我觉得整体上单看每一道题都不难,主要是题量的问题,看试卷评论最后很多人都最后一个编程题都空了,考场上时间紧,除非题目有要求,否则也不用去想时间复杂度最优的算法,怎么快怎么来。
六.西交915 2019研究生入学考试 编程题
1.二维平面上有n个点,输出一个长方形的左上角和右下角坐标,要求该长方形可以覆盖左右点。(略)
2.分别用递归和栈实现回文串判定。
递归实现:
#include
using namespace std;
string s;
bool check(int l,int r){
if(s[l]==s[r]){
if(l+1==r||l==r)return true;
return check(l+1,r-1);
}
return false;
}
void solve(){
int l=0,r=s.size()-1;
if(check(l,r))cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
main(){
while(cin>>s) solve();
}
栈实现:
#include
using namespace std;
string s;
bool check(){
stack<char> q;
for(auto c:s) q.push(c);
int i=0;
while(q.size()) {
if(q.top()!=s[i++])return false;
q.pop();
}
return true;
}
void solve(){
if(check())cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
main(){
while(cin>>s) solve();
}
3.O(nlogn)时间复杂度内求逆序对数量。
一眼树状数组求逆序对,其实归并排序也行在O(nlogn)内求出逆序对数量(参考答案给的是归并,不过答案建议直接暴力拿部分分,因为归并的代码不好写)
//树状数组
#include
using namespace std;
const int N=1e6+5;
int n;
int tr[N];
int a[N];
int lowbit(int x){
return x&-x;
}
int add(int x,int c){
for(int i=x;i<=N;i+=lowbit(i))tr[i]+=c;
}
int sum(int x){
int res=0;
for(int i=x;i;i-=lowbit(i))res+=tr[i];
return res;
}
main(){
cin>>n;
int maxx=0;
for(int i=1;i<=n;i++)cin>>a[i],maxx=max(maxx,a[i]);
int res=0;
for(int i=1;i<=n;i++){
res+=sum(maxx)-sum(a[i]);
add(a[i],1);
}
cout<<res<<endl;
}
七.西交915 2020研究生入学考试 编程题
1.a,b,c,d是四个0~9的数字,输出所有使得abcd+cadb=9102的数字
代码实现:
#include
using namespace std;
main(){
for(int a=0;a<=9;a++)
for(int b=0;b<=9;b++)
for(int c=0;c<=9;c++)
for(int d=0;d<=9;d++){
int x=a*1000+b*100+c*10+d;
int y=c*1000+a*100+d*10+b;
if(x+y==9102)printf("a b c d\n");
}
}
2.输如多组数据,一组数据有两个数a,b,判断其实是否满足反转的和等于和的反转。(ps:123的反转即321)
代码实现:
#include
using namespace std;
int n;
int rev(int x){
string s= to_string(x);
reverse(s.begin(),s.end());
return stoi(s);
}
main(){
cin>>n;
while(n--){
int a,b;
cin>>a>>b;
if(rev(a)+rev(b)==rev(a+b))cout<<a<<" "<<b<<endl;
else cout<<"none"<<endl;
}
}
3.200元全部来买n种物品,问有多少种搭配。
状态表示:f[i][j]表示已经选到了第i件物品,且当前选的商品中价值恰好为j元的方案数
状态转移:f[i][j]=f[i-1][j-w[i]]+f[i-1][j-2w[i]]+…+f[i-1][j-kw[i]]
吐槽:这个题真题BD上的题面描述有问题,单从题面上看说的应该是n件不同的物品,而且200元也没说要不要一定花完,但是根据样例,可以猜出来这是一个完全背包方案数的问题。
代码实现:
#include
using namespace std;
const int N=1000;
int n;
int a[N];
int f[N][N];
main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=200;j++){
int v=a[i];
for(int k=0;k*v<=j;k++){
f[i][j]+=f[i-1][j-k*v];
}
}
}
cout<<f[n][200]<<endl;
}
经过等价变形,代码还可以写成:
#include
using namespace std;
const int N = 1e6+5;
int n;
int a[N];
int f[N];
main(){
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
f[0]=1;
for(int i=0;i<n;i++){
for(int j=a[i];j<=200;j++)
f[j]+=f[j-a[i]];
}
cout<<f[200]<<endl;
}
八.西交915 2021研究生入学考试 编程题
1.给出一个矩阵,求所有在第i行最小且第j列最大的元素aij。
代码实现:
#include
using namespace std;
const int N=1010;
int n,m,a[N][N];
bool check(int x,int y){
int mx=0;
for(int i=0;i<n;i++)mx=max(mx,a[i][y]);
return mx==a[x][y];
}
main(){
cin>>n>>m;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>a[i][j];
set<pair<int,int> >s;
for(int i=0;i<n;i++){
int x=1e9;
for(int j=0;j<m;j++) x=min(x,a[i][j]);
for(int j=0;j<m;j++){
if(x==a[i][j]){
if(check(i,j)) s.insert({i,j});
}
}
}
for(auto t:s)cout<<t.first<<" "<<t.second<<endl;
}
2.定义完全数公式: 2 p − 1 ∗ ( 2 p − 1 ) 2^{p-1}*(2^p-1) 2p−1∗(2p−1),p为素数,输出10000以内所有的完全数,并输出相应的p。
素数筛板子,注意这题说明西交最近两年考过数论,记得后期复习
em…不过说实话,因为2的32次方就超出int范围了,所以当然直接求暴力素数也行
#include
using namespace std;
const int N=50;//2的32次方就超出int了
vector<int>prime;
bool st[N];
void init(){
for(int i=2;i<N;i++){
if(!st[i])prime.push_back(i);
else continue;
for(int j=i+i;j<N;j+=i)st[i]=true;
}
}
int get(int x){
return (1<<(x-1))*((1<<x)-1);
}
main(){
init();
for(auto x:prime){
int k=get(x);
if(k>=10000)break;
cout<<k<<" "<<x<<endl;
}
}
3.最开始有一个机器人,每个机器人在出生三天后(第四天成熟开始)每天可以生一个机器人一次。求第n天的机器人数量。(最开始六天的机器人数量:1 2 3 4 6 9)
模拟即可,机器人一共可以分为四种情况,出生1,2,3天和成熟
#include
using namespace std;
int n,a[4];
main(){
cin>>n;
a[3]=1;
int res=0;
for(int i=0;i<n;i++){
int x=a[3];
res+=a[3];
a[3]+=a[2];
a[2]=a[1];
a[1]=a[0];
a[0]=x;
}
cout<<res<<endl;
}
九.西交915 2022研究生入学考试 编程题
1.C++自定义排序(略)
2.输入一组数判断是否连续(略)
3.01背包输出具体方案(给了一堆价值,求所有选出后的总价值恰好等于m的具体方案)。
状态表示:f[i][j]表示已经选到了第i个物品,且当前恰好选了价值为j的状态,f[i][j]为1表示该状态存在,f[i][j]为0表示该状态不存在。
状态转移: f [ i ] [ j ] = f [ i + 1 ] [ j − w [ i ] ] ∣ f [ i + 1 ] [ j ] f[i][j]=f[i+1][j-w[i]]|f[i+1][j] f[i][j]=f[i+1][j−w[i]]∣f[i+1][j]
PS:DP输出方案类似于最短路,状态转移完成后倒着输出就行了。另外这里再吐槽一下,我买的真题都是回忆版的,根据上面的答案能看出来是再求01背包方案数,但是我问22上岸的学长说的是输出的是具体方案。鉴于之前考过求方案数的问题,这里给出具体方案的求解方案。
代码实现:
#include
using namespace std;
const int N=1010;
int n,m;
int w[N];
int f[N][N];
main(){
cin>>n>>m;
for(int i=0;i<n;i++)cin>>w[i];
f[n][0]=1;
//因为后面我想直接按字典序输出,所以这里倒着转移
for(int i=n-1;i>=0;i--){
for(int j=0;j<=m;j++){
if(j>=w[i])f[i][j]=f[i+1][j-w[i]];
f[i][j]=f[i+1][j];
}
}
int k=m;
for(int i=0;i<n;i++){
if(k>=w[i]&&f[i][k]){
cout<<i<<" ";
k-=w[i];
}
}
}
十.西交915 2023研究生入学考试 编程题
一.for循环(略)
二.暴力矩阵求和(略)
三.走楼梯求方案数(略)
PS:我在考场上记得应该是这些题。这些题都很简单,这里就不写代码了吧,已经上岸啦hhhhh。