链接:https://ac.nowcoder.com/acm/contest/553/G 来源:牛客网 Chino with Train to the Rabbit Town 时间限制:C/C++ 1秒,其他语言2秒 题目描述Chino的数学很差,因此Cocoa非常担心。这一天,Cocoa准备教Chino学习异或。
异或还有一些非常有趣的性质,比如 输入描述: 输出描述: 示例1 输入复制 输出复制 说明 | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
直观解法应该是用动态规划:
dp[i]表示处理到第i个人时可以得到的最优解,针对第i个人,我们可以向前搜索是否有一个下标为j的值a[j]使得a[j]-a[i]的异或值为k,如果存在则
dp[i] = max(dp[i-1], dp[j-1]+1);
如果不存在
dp[i] = dp[i-1];
dp[i-1]保证了最优解的存在,即a[j]-a[i]虽然异或值为k,但仍有不被分到一个车厢内的可能。
这样的时间复杂度是O(n^2),会爆掉,那么就要在搜索j时做一些处理了,这样就用到异或的一个性质。
若存在a,b,c,有a^b = c;
则a^c = b 且 b^c = a;
那么我们用一个sum运算出每次添加新元素的异或值,并用一个pre[sum]存下每次sum对应的i值,这样sum^k就会得到我们所需的下标j。
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 5e5+50;
int main(void)
{
int n,k,x,sum;
dp[MAX_N],pre[MAX_N];
memset(pre,-1,sizeof(pre));
sum = dp[0] = pre[0] = 0;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
{
scanf("%d",&x);
sum ^= x;
if(pre[sum^k] != -1) //pre[sum^k]表示所需的下标
dp[i] = max(dp[i-1],dp[pre[sum^k]]+1);
else dp[i] = dp[i-1];
pre[sum] = i;
}
printf("%d",dp[n]);
return 0;
}
贪心法:
动态规划很直观,但贪心法就又快又好,贪心的思想是尽可能的多凑出k,如果我们在前i个里面找到一个k,那么后面的值是否有k对前面的并不影响,所以我们还是不断的向前找下标即可,即使后面的出现的值要用到前面才能凑出k也不必管,因为我们本来就有一个k了,现在牺牲更多的元素凑出来的还是一个k并无益处。
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 5e5 + 7;
map<int, int> mp;
int main() {
int n, k;
int a[MAX_N];
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
int ans = 0;
mp[0] = 1; //做标记
int temp = 0;
for (int i = 1; i <= n; i++) {
temp ^= a[i];
if (mp[temp^k]) {
ans++;
mp.clear(); //前面的找到k清空
mp[0] = 1; //以当前位置做新的起点
temp = 0;
}
else
mp[temp] = 1;
}
printf("%d", ans);
return 0;
}