复旦2021年机试上机题目学习

2 篇文章 0 订阅

        虽然是半个计算机科班出身,很惭愧地没在大一大二打好C/C++基础,在此前提下,开始看复旦2021年机试上机题目进行学习,若有代码未能准确优化的地方,请各路大佬们一定要积极指出,对我也是莫大的帮助!


复旦2021年第二题爬楼梯

解释:这是一道比较基础的动态规划问题,我一眼就看出了状态转移方程(啊不是,dp都基本记不得了,真是惭愧,我通过找规律的方式发现了其中的奥妙)

当我们尝试走到第k级台阶上的时候,我们有两种方案可以选择:

1、从第k-2级台阶选择爬上两步

2、从第k-1级台阶选择爬上一步

假设到达第k级台阶可能的步数为dp[k],那么很明显有状态转移方程:

dp[k]=dp[k-1]+dp[k-2]

这是因为,到达k-1级台阶的可能步数只需再走一步"2级台阶”便能到达k级台阶,而可能步数的数量不变,同理可知k-2级台阶的可能步数数量。

公式有前提:k>=2,所以必须初始化dp[0]=0(在陆地上没有走的方案)、dp[1]=1(走到第一级台阶为一种可能),但是k=2时不符合公式,所以再补上dp[2]=2即可,循环从3开始即可。

代码环节:

#include<iostream>

using namespace std;

const int N=1e5+10;
int dp[N];//可能步数数组 
int main()
{
	int n;//楼梯数量 
	cin>>n;
	dp[0]=0;
	dp[1]=1;
	dp[2]=2;//当然为了配合公式可以选择让dp[0]=1,但是逻辑上不是很能说服自己,就选择再多初始化一个
	for(int i=3;i<=n;i++)	dp[i]=dp[i-1]+dp[i-2];	//状态转移方程 
	cout<<dp[n];
	return 0;
}

2022.2.24更新

今天看到舍友在群里发了一道吃豆子的算法面试题目,看了一眼就发现和这道走楼梯差不多,拿到这里来做个类比,当然没有正确答案,如果有大佬发现错误可以及时向我指出!谢谢!

题目是这样的:

 我想的大体思路如下:

首先是要有个动态规划数组dp[k],表示吃k颗豆子的可能情况数量,分以下两种情况:

1、从第k-2颗豆子吃两颗变为k颗

2、从第k-1颗豆子吃一颗变为k颗

第二种情况中dp[k-1]代表了吃k-1颗豆子的情况,每种情况吃一颗就变为k颗,可以直接用。

而第一种情况中dp[k-2]中的所有情况并不能全部算上,因为只有最后一次吃一颗豆子的才能算上,否则将违反连续吃两次2颗豆子的游戏规则。

那么在dp[k-2]中,最后一次吃一颗豆子的可能性为dp[k-3],即为所有吃k-3颗豆子后,最后一颗直接吃的情况。

故有状态转移方程如下:

dp[i]=dp[i-1]+dp[i-3]

那么在此前提下,需要提前规定dp[1]=1,dp[2]=2,dp[3]=3。代入方程检验一下发现dp[4]=dp[3]+dp[1]=4,题目中多给了一个条件作为演算。

代码部分特别容易,可以直接写出:

#include<iostream>
using namespace std;
const int N=1010;
int n;//总共有多少颗豆子 
int dp[N];//dp数组 
int main()
{
	cin>>n;
	dp[1]=1;//一颗豆子一种吃法 
	dp[2]=2;//两颗豆子两种吃法 
	dp[3]=3;//三颗豆子三种吃法 
	for(int i=4;i<=n;i++)
	{
		dp[i]=dp[i-1]+dp[i-3];//状态转移方程 
	}
	cout<<dp[n];
	return 0;
} 

复旦大学2021机试第一题题解:

(不一定正确,简单试了一下demo是对的,望各位大佬发现问题及时指正!)

简单的思路:

感觉第一题还是挺简单的一道题目,我就是用暴力搜索的方法,开一个大型数组,每一次检索所有ancestor即可,一开始把根节点直接加入,以免后面的判断出现问题。

代码部分如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N=1e5+10;

int t[N],flag[N];

int stringtoint(string a)
{
	int res=0;
	for(int i=0;i<a.size();i++)
	{
		int p=a[i]-'0';
		res=res*10+p;
	}
	return res;
}

int main()
{
	string s;
	int i=1;//root number = 1 
	memset(flag,-1,sizeof flag);//set flag to -1
	while(cin>>s)
	{
		if(s=="null")	t[i]=-1;
		else		t[i]=stringtoint(s);
		i++;
		if(cin.peek() == '\n') { break; }
	}	
	int res=0; //cout number
	flag[1]=1;//root flag is 1,else the loop will be dead
	for(int j=1;j<i;j++)
	{
		if(t[j]!=-1)
		{
			int m=j/2;
			while(m!=0)
			{
				if(t[m]>t[j])	//if any ancestor is large, break
				{
				break;
				}
				m/=2;
			}
			if(m==0)	flag[j]=1;//m have adjusted to 0, all route has been scaned
		}
	}
	for(int k=1;k<i;k++)
	{
		if(flag[k]!=-1){
		res++;
		cout<<t[k]<<endl;	
		}
	}
	cout<<res;
	return 0;
}

 


复旦大学2021年机试第三题题解:

思考部分如下:

又是一道dp的题目,但是状态转移方程其实很好想,这是一道类似于背包问题的题目,例如思考dp[i][j]为前i个数字通过正负转换加起来得到期待和E=j的个数,那么我们获得这个个数有两个选择,一个是第i个数字取正数,那么前i-1个数字的期待和E=j-a[i],同理,第i个数字取负数,那么前i-1个数字的期待和就应该为E=j+a[i]。状态转移方程就可以得到为:dp[i][j]=dp[i-1][j-a[i]]+dp[i-1][j+a[i]]

但是用这个方法思考,就可能出现背包容量为负数的情况,比如存在第一个数为负数但是没办法下标放到数组中。所以,我们一开始在输入数字的时候可以统计所有数字的和,开辟一个总和*2的数组,将一倍的sum作为原先的期待E=0的情况,所有的判断基于此为中心。当然,一开始我们需要初始化我们的dp数组,将第一行全部初始化,即选第一个数字的情况,当第一个数字为0时,将dp[1][sum]的值设置为2(因为存在+0和-0两种情况),如果第一个数字不是0的话, 那就将dp[1][sum+a[i]]和dp[1][sum-a[i]]的值都设置为1,接下来按照上述状态转移方程计算即可,不明白的可以画一个dp的草图帮助理解。

代码部分如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N=1e5+10;

int a[N];											// for number
int main()
{
	int i = 1 , sum = 0;
	memset(a,-1,sizeof a);							//set a to -1
	while(cin >> a[i])
	{
		sum += a[i];
		i++;
		if(cin.peek() == '\n') { break; }
	}
	sort(a + 1,a + i - 1);
	int e = a[i - 1];								// our expectation
	sum -= e;										// minus expectation
	int dp[i + 10][2 * sum + 10];					//dp array
	memset(dp,0,sizeof dp);							// dp initialization
	for(int k = 0;k <= 2 * sum ;k++)
	{
		if(k == sum - a[1] || k == sum + a[1])	
			{if(a[1] != 0) dp[1][k] = 1;
			else	dp[1][k] = 2;
			}
		else dp[1][k] = 0;
	}												//first line will be set
	for(int p = 2;p < i - 1 ; p ++)
	{
		for(int q = 0;q <= 2*sum;q++)
			{if(q>=a[p])	dp[p][q] += dp[p - 1][q - a[p]];
			dp[p][q] += dp[p - 1][q + a[p]];
			cout<<" p:"<<p<<" q:"<<q<<" dp:"<<dp[p][q]<<endl;}
	}
	cout<<dp[i - 2][e + sum];
	return 0;
}

接下去要做2020年的了!

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值