蓝桥杯国赛——对局匹配

题目

此题本质上是一个01背包问题(提高课状态机模型)(花了好长时间才看懂我是废物)

题目大意:
对于x1,x2…xn来说,从中挑出一些数,使得序列中的数互相之差不等于K.

分析:
可以把序列中的数分为两类
1.对于数X可以找到X+k或X-k.
2.对于数X找不到X+k或X-k.

先看第二类:对于x+k,x-k不存在,所以这种情况的每一个数都符合题目的要求,直接加到答案中,然后删去这些数即可.

对于第一类又可以分为两类:
1.对于序列中某一段x1,x2...xn,由于其差都为k,所以可以构成一个等差数列.
2.对于序列中某一段可能存在多条等差数列.(解决:将序列中的等差数列预处理出来后保存,这样可以将这段序列转化成P条等差数列)

对于第K条等差数列x1,x2,x3..xn中的数xi ,其都有选或者不选两种方案.
设状态定义为:f[i][3]选到第i个数,且第i个数选或者不选所构成非等差序列的最长长度.(即从等差序列中选出最长的非等差序列)

根据题目限制:
1.当不选第i个,则第i-1个选或着不选.
2.选了第i个,则第i-1个一定不能选
状态转移方程:

f[i][1]=max(f[i][1],f[i-1][0]+num[b[k][i]]);//b[k][i]表示第k段等差数列中的第i个元素
f[i][0]=max(f[i][0],max(f[i-1][0],f[i-1][1]));

注意:本题需要去重,不去重的话还得把所有相同数的答案加起来导致不好处理.

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;

const int N=2e5+10;

//本质:多个01背包

int n,k;
int num[N];//每个元素出现的次数
int temp[N];//初始数组
vector<int> a;//排除无关元素后的数组
vector<int> b[N];//存储k段等差数列
int cnt;
int res;//最终答案

int main()
{

   cin>>n>>k;
   for(int i=1;i<=n;i++) cin>>temp[i],num[temp[i]]++;
   
   for(int i=1;i<=n;i++) 
   if(num[temp[i]+k]||(temp[i]>k&&num[temp[i]-k])) a.push_back(temp[i]);
   else res++;//第二类
   
   sort(a.begin(),a.end());
   a.erase(unique(a.begin(),a.end()),a.end());//去除无关元素,留下来一定是可以配成<x-k,x>的元素
   

   //预处理出所有的等差数列
   int st[N],len=a.size();
   memset(st,0,sizeof st);
   for(int i=0;i<len;i++)
   {
       if(!st[a[i]])
       {
       int t=a[i];                  
       while(num[t]>0&&!st[t]) b[cnt].push_back(t),st[t]=1,t+=k;
       cnt++;
       }
   }

  //对每段等差数列做一遍01背包
  for(int i=0;i<cnt;i++)
  {
      int f[N][3];//f[i][0]不选第i个元素,f[i][1]选第i个元素
      int size1=b[i].size();
      memset(f,0,sizeof f);
      for(int j=0;j<size1;j++)
      {
          f[j][1]=max(f[j][1],f[j-1][0]+num[b[i][j]]);
          f[j][0]=max(f[j][0],max(f[j-1][0],f[j-1][1]));
      }
       
       res+=max(f[size1-1][0],f[size1-1][1]);
  }
  
  cout<<res;

    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值