Chino with Train to the Rabbit Town-贪心或动态规划

 

链接:https://ac.nowcoder.com/acm/contest/553/G
来源:牛客网
 

Chino with Train to the Rabbit Town

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

Chino的数学很差,因此Cocoa非常担心。这一天,Cocoa准备教Chino学习异或。
众所周知,,即“a异或b”表示了ab的二进制按位异或的结果(在C/C++中,表示了异或运算。),它的规则是如果这一位相同为0,否则为1.例如,,因为,根据定义,它们之间的异或值是,下面是异或运算的真值表:

AB
000
011
101
110

异或还有一些非常有趣的性质,比如之类的。定义很简单,Chino也一下就学会了,那么现在就是作业时间啦!
开往兔子镇的火车一开始还是手摇式的木板车,所有人都在木板上做成一排,当然,就像你想的那样,旅途非常尬。如今,铁路修好了,因此人们可以坐火车来到兔子镇。有一个问题就是怎么划分车厢——大家都希望能够单独一个车厢,但在大部分情况下这是做不到的。
火车上有个乘客,坐在第i位的乘客对车厢的划分有一个意愿值,我们定义一节车厢的总意愿值为这节车厢所有人意愿值的异或和,即,如果这节车厢包含了第名乘客,那么这节车厢的意愿值是.特别地,如果这节车厢只有一名乘客i,那么这节车厢的意愿值就是a_i.这个意愿值当然越高越好,但是这会让当局非常难办,因此他们确定了一个标准kk的范围介于所有可能出现的意愿值之间。现在的任务就是让尽可能多的车厢的意愿值为k.
题目对Chino来说太难啦,你能帮一帮Chino吗?

输入描述:

第一行是两个数n, k;接下来一行是n个数ai

输出描述:

题目中要求的答案

示例1

输入

复制

3 1
1 2 3

输出

复制

2

说明

我们划分成[1]和[2,3]两段,显然每段的异或和都是1.注意,同一位乘客只能被划分在一节车厢中,即,你不能做出类似[1,2],[2,3]的划分
  
   
   
   
   

直观解法应该是用动态规划:

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值