百度 2024届暑期实习生招聘 机器学习/数据挖掘工程师/自然语言处理

最少异或次数

小A最近学习到一种新的运算,叫异或,运算符记为xor。这是对两个同长度的二进制串
(仅含有0或1的字符串)进行的运算,相同位上对应的数字如果不同则该位运算结果为1,否则为0。
例如,现在小A手上有一个二进制串s,他想让这个二进制串异或上若干个长度相等且所有的1连续的,
二进制串(如000111,1100,010,1等,但01010,101等不合法),使得S所有位都为0。
小A想知道最少需要讲行多少次异或运算?

输入描述

第一行一个正整数T,表示有T组数据;
接下来每一组数据对应一行,首先输入x,表示该二进制串长度;之后输入一个长度为x的二进制串。中间用空格隔开。
1<=x<=5*10^4  1<=T<=100

输出描述

输出T行,每一行一个整数,表示该数据下的最小异或次数。

样例输入

2
8 00011101
1 0

样例输出

2
0

提示

样例解释
第一组数据:一种方法是异或上00000010和00011111即可。
第二组数据:原串已经为0,不需要进行异或操作。

思路:其实本题只是在问给定的二进制字符串中有少段连续的1,当然具体要怎么抽象到这一步结论,且听我分析:

假设原串上存在两段连续1,即原串可以表示为1...10...01...1,我们发现有两段连续区间,那么按照上面的思路,我们其实只需要两次抑或。那么有没有其他想法呢?也许我们可以尝试先将中间的0异或成1,这样就是全1的二进制串,再抑或一次即可。可是这样做并不能少于两次,所以说,其实他这个样例提示就是故意带偏人的,小心陷阱啊!!!

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

int main(){
	int T, n, oneIntervalNum;
	bool intoOneInterval;
	string binaryStr; 
	char ch;
	cin >> T;
	while(T--){
		cin >> n >> binaryStr;
		intoOneInterval = false;
		oneIntervalNum = 0;
		for(char ch:binaryStr){
			if(ch == '1'){
				intoOneInterval = true;
			}else{
				intoOneInterval ? ++ oneIntervalNum : 0 ;
				intoOneInterval = false;
			}
		}
		intoOneInterval ? ++ oneIntervalNum : 0 ;
		cout << oneIntervalNum << endl;
	} 
} 

删除游戏

现在给你一个包含n个正整数的序列a,你可以进行许多次直到序列为空,每次操作可以选定当前
序列中的一个数{ai}并删除,然后删除所有等于{ai}+1或者{ai}-1的数,删除的数无法再被之后
的操作选中,这样的一次操作能让你获得{ai}分,请问你最多能获得多少分数。

输入描述

第一行一个正整数n,表示序列a的长度。
接下来一行包含n个正整数{a1},{a2},......{an},分别为序列a中的n个数。
n<=100000,1<={ai}<=100000

输出描述

输出可以获得的最高分数。

样例输入1

3
1 2 3

样例输出1

4

 样例输入2

5
1 2 3 2 2

样例输出2

6

提示

输入样例2
5
1 2 3 2 2
输出样例2
6

样例说明
对于样例2,第一次可以选择删除任意一个等于2的数,这次操作同时删除等于1和3的数,
获得2分且序列中剩余的数为[2,2],之后可以连续两次操作都选择等于2的数,这样总共能获得6分。

思路:对于每种数nums[i]而言,当我们选取的时候,肯定要把所有的nums[i]都选取。于是问题转换为了选取哪几种数可以获得最多的分数,我们用nums[i]表示第i种数,num_count[nums[i]]表示nums[i]的个数,那么选取nums[i]对答案的贡献为nums[i]*num_count[nums[i]],关于如何做到不选nums[i]-1与nums[i]+1,其实我们只需要做到不选取num[i]+1即可,我们对nums进行排序,当选取nums[i]的时候,如果nums[i+1]==nums[i]+1,则就可以直接从i+1开始进行后续的选取。当然需要注意的是越界问题。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 7;
int arr[maxn], num_count[maxn], max_score, n;
bool existed[maxn];
vector<int> nums;

void DFS(int id, int score){
	if(id >= nums.size()){
		max_score = max(max_score, score);
		return;
	}
	if(id + 1 < nums.size() && nums[id] + 1 == nums[id + 1]){
		DFS(id + 2, score + nums[id] * num_count[nums[id]]);
	}else{
		DFS(id + 1, score + nums[id] * num_count[nums[id]]);
	}
	DFS(id + 1, score);
}

int main(){
	int idx;
	cin >> n;
	for(idx = 0; idx < n; ++ idx){
		cin >> arr[idx];
		++ num_count[arr[idx]];
		if(!existed[arr[idx]]){
			existed[arr[idx]] = true;
			nums.emplace_back(arr[idx]);
		}
	}
	sort(nums.begin(), nums.end());
	DFS(0, 0);
	cout << max_score;
	return 0;
} 

当然,本题还可以用后缀和进行剪枝,具体来说,当我们判断nums[i]是否要选取的时候,可以直接看当前的score累加上后面所有未选择过的数都不能大于之前所得到的最大分数的话,当前分支就可以不走了。考虑到这种优化在前面的数大后面的数小的时候才能有效剪枝,因此我们倒序排序数组,修改一下代码 id + 1 < nums.size() && nums[id] - 1 == nums[id + 1]。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 7;
int arr[maxn], num_count[maxn], suffix_sum[maxn], max_score, n;
bool existed[maxn];
vector<int> nums;

void DFS(int id, int score){
	if(id >= nums.size()){
		max_score = max(max_score, score);
		return;
	}
	if(score + suffix_sum[id] < max_score){
		return;
	}
	if(id + 1 < nums.size() && nums[id] - 1 == nums[id + 1]){
		DFS(id + 2, score + nums[id] * num_count[nums[id]]);
	}else{
		DFS(id + 1, score + nums[id] * num_count[nums[id]]);
	}
	DFS(id + 1, score);
}

int main(){
	int idx;
	cin >> n;
	for(idx = 0; idx < n; ++ idx){
		cin >> arr[idx];
		++ num_count[arr[idx]];
		if(!existed[arr[idx]]){
			existed[arr[idx]] = true;
			nums.emplace_back(arr[idx]);
		}
	}
	sort(nums.begin(), nums.end(),[](int a, int b){
		return a > b;
	});
	suffix_sum[nums.size()] = 0;
	for(idx = nums.size() - 1; idx >= 0; -- idx){
		suffix_sum[idx] = suffix_sum[idx + 1] + nums[idx] * num_count[nums[idx]];
	}
	DFS(0, 0);
	cout << max_score;
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值