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的合理划分方法,因此可以用回溯法求解。假设字符串s长度为n,则当第i(i<n)个字符要么单独划分为一个数,要么和i+1一起划分为一个数;而第n个字符只能单独划分为一个数。因此可采用回溯法,向下一层左子树搜索的约束条件就是由第i个字符与第i+1个字符结合得到的数字落在1到n的范围内且之前未被划分出来,向下一层右子树搜索的约束条件就是由第i个字符单独形成的数字落在1到n的范围内且之前未被划分出来。如若当前字符为'0',说明之前的划分有误(因为'0'既不能和后一个字符结合,也不能单独成数),应该直接返回上一层。如何知道已经找到了正确的划分呢?只需要在搜索到叶子结点时检验是不是已经划分出n-1个数字即可。

因此划分和回溯就出来了:

1、当前字符下标等于n时,划分结束,判断当前是否找到了n-1个并输出

2、当前字符下标等于n-1时,只有单独划分的情况,递归调用(下标+1)

3、当前字符下标小于n-1时,有两种划分情况:

     1) 单独划分,递归调用(下标+1)

     2) 与后一个一起相组划分,递归调用(下标+2)

#include <iostream>
using namespace std;
int a[41] = {0};   // 记录这些数字有没有被找到 
bool flag = 0;     // 标记找到没,只找一个数 
void find(int findnum, string s, int index, int n){
	int length = s.length();
	if(flag == 1) return;
	if(index == length){
		// 判断有无符合要求的返回
		if(findnum == n-1){
			for(int i=1; i<=n; i++)
				if(a[i] == 0)
					cout<<i<<endl;
			flag = 1;
		}
	}
	if(index == length - 1){   // 最后一个数只能单独成数 
		// 单独成数
		if(a[s[index] - '0'] == 0 && s[index] != '0' && findnum<=n-1 && (s[index]-'0')<=n){
			a[s[index] - '0'] = 1;
			find(findnum+1, s, index+1, n);
			a[s[index] - '0'] = 0;  // 回溯 
		}
	}
	if(index < length - 1){
		// 单独成数
		if(a[s[index] - '0'] == 0 && s[index] != '0' && findnum<=n-1 && (s[index]-'0')<=n){
			a[s[index] - '0'] = 1;
			find(findnum+1, s, index+1, n);
			a[s[index] - '0'] = 0;  // 回溯 
		}
		// 与后一个成数
		if(s[index] != '0' && findnum<=n-1){
			int temp = (s[index] - '0')*10 + (s[index+1]-'0');
			if(temp<= n && a[temp] == 0){
				a[temp] = 1;
				find(findnum+1, s, index+2, n);
				a[temp] = 0;  // 回溯 
			}
		}
	}
}
int main (){
	int n;
	cin>>n;
	string s;
	cin>>s;
	find(0, s, 0, n);
	
	return 0;
} 

参考博文:https://blog.youkuaiyun.com/climber16/article/details/81604342

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值