南京大学历年上机题汇总

2018夏令营机试

本地生

1、Count number of binary strings without consecutive 1’s

Given a positive integer n(3≤n≤90), count all possible distinct binary strings of length n such that there are no consecutive 1’s .

Examples:

  Input:  2 
  Output: 3 // The 3 strings are 00, 01, 10 
  ​
  Input: 3 
  Output: 5 // The 5 strings are 000, 001, 010, 100, 101

中文题意:给定一个正整数n(3≤n≤90),数出长度为n的所有可能的不同二进制串的个数,使得串中没有连续的1出现。

首先考虑到可以使用回溯法解答,但看大佬说会超时。
又注意到本题只需要返回个数,没有要返回每一个具体的值,因此考虑使用dp来解答。

注意:能使用dp的尽量不要使用回溯做,太容易超时。

设dp[i]表示的是n为i时的可能的长度的个数。dp[i]计算时,需要考虑当前这一位置是0还是1,如果是1的话,那么前面一位必然为0,只需要考虑i-2位,即dp[i-2];如果是0的话,那么前面一位可以为1也可以为0,没有限制。两种情况相加可以得知dp[i] = dp[i-1] + dp[i-2]。

注意:返回值可能过大,需要使用long long类型

2、Missing number

Given a positive integer n(n≤40), pick n-1 numbers randomly from 1 to n and concatenate them in random order as a string s, which means there is a missing number between 1 and n. Can you find the missing number?(Notice that in some cases the answer will not be unique, and in these cases you only need to find one valid answer.)

Examples:

  Input: 20
         81971112205101569183132414117
  Output: 16

中文题意:给定正整数n(n≤40),从1到n中随机选择n-1个数,并将它们以随机顺序连接为字符串s,这意味着在1和n之间有一个缺失的数字。你能找到那个缺失的数字吗?(请注意在某些情况下答案不唯一,此时你只需要找到一个有效的答案。)

需要注意到的是它给的是这些字符拼接而成的字符串s,需要将这个字符串拆分出来。然后就变成了一个从1-n的数组缺失一个元素的题目,通过异或1-n求解。

拆分则使用回溯法,通过剪枝(>40/重复出现)来减少次数。
由于出现的元素是连续的,因此可以使用一个vector来记录出现过的元素。

#include<iostream>
#include<vector>
#include<string>
using namespace std;

void dfs(vector<int> isExist, string s, int p, int n, vector<vector<int>>& ret,int cnt);

int main() {
	int n;
	string s;
//	cin >> n;
//	cin >> s;
	n = 20;
	s = "81971112205101569183132414117";
	n = 27;
	s = "3725816181026271711142021232413191295615224";
	//开始回溯
	//可以使用vector记录每一个数字是否使用,因为是固定的若干个数字
	vector<int> isExist(n + 1, 0);
	vector<vector<int>> ret;
	dfs(isExist, s, 0, n, ret,0);
	cout << "ret.size = "<< ret.size() << endl;
	//开始找到那个不在的值
	int ans = 0;
	//对每一个结果进行遍历
	for (int t = 0; t < ret.size(); t++) {
		//踩坑,当要处理一个新的值的时候,需要记住更新初值为0;
		ans = 0;
		vector<int> cur = ret[t];
		for (int i = 1; i <= n; i++) {
			if (cur[i] == 1)
				ans = ans ^ i;
			ans = ans ^ i;
		}
		cout << "ans = " << ans << endl;
	}
	

}

//dfs回溯算法
void dfs(vector<int> isExist,string s,int p,int n,vector<vector<int>>& ret,int cnt) {
	cout << "进入dfs, p = " << p << ",n = " << n << endl;
	//p表示当前从哪个元素开始处理
	//n是可能出现的最大的数字
	//边界条件
	if (p >= s.size()) {
		if (cnt == (n - 1)) {
			cout << "p == s.size()" << endl;
			//已经遍历完所有的了
			ret.push_back(isExist);
		}
		return;
	}
	

	//有两种可能
	//1、该位自成一位
	int tmp = s[p] - '0';
	cout << "tmp = " << tmp << endl;
	if (tmp <= n && tmp >= 1 && isExist[tmp] == 0 ) {

		cout << "可以由一位组成" << endl;
		isExist[tmp] = 1;
		dfs(isExist, s, p + 1, n,ret,cnt+1);
		isExist[tmp] = 0;

	}
	//2、该为是由两位组成
	if ((p + 1) >= (s.size()-1))return;

	tmp = stoi(s.substr(p, 2));
	cout << "tmp = " << tmp << endl;
	if (tmp >= 10 && tmp <= n && isExist[tmp] == 0) {
		cout << "可以由两位组成" << endl;
		//可以继续
		isExist[tmp] = 1;
		dfs(isExist, s, p + 2, n, ret,cnt+1);
		isExist[tmp] = 0;
	}
	return ;
}

踩坑:
1、注意这些从字符串中提取数字的东西,很多都要求不能为0,或者是02这样的形式。
2、注意这题要求的只缺失一个数字,因此还需要计数,只有到了指定的n-1个元素才能返回,不然可能出现缺失的是一个两位数,但是把它当作了两个一位数字。
如:把3725816181026271711142021232413191295615224,
拆分为 3 7 25 8 16 18 10 26 27 17 11 14 20 21 23 24 13 19 12 9 5 6 15 22 4
所以要好好读题呀!

2019年夏令营

1、输入一串数字,移除 k 个数字,数字相对位置不变,使得剩下的数字组成最小的数。

输入:int
输出:int

思路:使用贪心的方法,每次删除一个数字,使得剩余的数字最小。问题就转换为如何找这个应该删除的元素。

  • 从2个开始 12,21,应该删除的都为2.
  • 当有三个元素时,如果第一个比第二个元素大,
    abc 删除一个元素后为ab、bc、ac,
    若a>b 则一定删除a使得元素最小。
  • 再举个例子 abcde
    bcde acde abce abcd
    如果a>b则一定删除a,如果a<b && b>c 则删除b。
  • 结论:从前往后,找到第一个递减的地方,就把那个大值删掉。
  • 那如果全都是递增,则删除最后一个元素。
#include<iostream>
#include<string>
using namespace std;

int deleteOneNumber(int n);

int main() {
	int n;
	int k;
//	cin >> n;
//	cin >> k;
	n = 9128456;
	k = 2;
	//开始分为k次进行处理
	for (int i = 0; i < k; i++) {
		//每一次删除元素中一个值,使得剩下的最大
		cout << "进入轮次:" << i << endl;
		n = deleteOneNumber(n);
	}
	cout << "n = " << n << endl;
	return n;
}

int deleteOneNumber(int n) {
	cout << "n = " << n << endl;
	//把int转换为str,再转为char
	string str = to_string(n);
	if (str.size() <= 1)return 0;
	int next;
	int cur;
	for (int i = 0; i <= str.size()-2 ; i++) {
		cur = str[i] - '0';
		next = str[i + 1]-'0';
		if (cur > next) {
			cout << "需要删除当前元素了,其为"<<cur<< endl;
			//需要删除前面一个元素了
			str.erase(i, 1);
			int ret = stoi(str);
			cout << "ret= " << ret<<endl;
			return ret;
		}
		else {
			if (i == (str.size()-2)) {
				str.erase(i+1, 1);
				int ret = stoi(str);
				return ret;

			}
		}
	}
	return 0;
}

注意:里面使用了int to string 只用了to_string()函数,string转int一般使用stoi函数,string转char使用c_str函数,char* 转int使用ctoi函数。

有B个男孩,G个女孩,要求所有男孩女孩排成一队,连续的男孩个数不可以超过K个,问一共有多少种排法。(结果需要mod 10007)

第一个想到的是回溯法,但看到问一共有多少排法,并且要求mod,复杂度比较高,还是先考虑一下dp。
dp:由于出现了三个变量,首先要考虑一下使用二维的还是三维的dp。

  • 先使用二维的尝试一下,B x G的数组,dp[i][j]表示有i个男孩,j个女孩时,连续男孩不超过k个的排法。
    注意:本题可以转换为G+1个抽屉(每个女孩身边可以插入的位置),B个球,每个抽屉中最多可以放K个球(最多连续K个男孩)。
    那么dp[g][b]表示有g个抽屉,b个球时的结果。注意到它与dp
    [g-1]时的关系,注意到dp[g]比dp[g-1]增加了一个抽屉,这个抽屉最多可以放min(b,k)个球。那么就把它转换为了在最后一个抽屉放i个球时,前面g-1个抽屉的放个数为b-i的可能性总和。
    dp[g][b] = dp[g-1][b-0]+dp[g-1][b-1]+…+dp[g-1][b-min(b,k)]
    依据此展开dp的代码。
  • 那么使用三维数组来看看,BxGxK
    dp[i][j][k] =

其他说明

南大的机试分两场,南京本地一场、外地一场。2019年的外地同学的机试难
度略高于本地同学机试难度。机试共3题,按照套路一般是动态规划、深广度遍历、树这些题目,大概是leetcode中等难度题,模板题比较多。(南大年年有DP!!!)dp解法想不到的话,可以靠dfs骗分。

参考了很多大佬们的文章

————————————————
版权声明:本文为CSDN博主「Legends Never Die」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/climber16/article/details/81604342/
————————————————
版权声明:本文为CSDN博主「Shaft_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shaft_/article/details/96226294
————————————————

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值