跳到主要内容

题解:P12687 [KOI 2022 Round 1] 鹅卵石

· 阅读需 2 分钟
lailai
Blogger

原题链接

题意简述

给定长度为 nn 的数组 aa,每次可对相邻两点同时拿相同数量,或对单点拿任意数量,求将所有石子拿完时所需的最少操作次数。

解题思路

  1. 枚举每个起点 ii,向右模拟交替相邻配对:维护一个变量 tt,初始为 aia_i,然后依次计算 t=ajtt=a_j-t

  2. t>0t>0 且恰好等于 aj+1a_{j+1},则区间 [i,j+1][i,j+1] 可以完全通过相邻配对消除,记录其右端点为 jj。否则若 t0t\le 0 则跳出,尝试下一个起点。

  3. fif_i 表示在位置 [1,i+1][1,i+1] 范围内,最多能选的互不重叠“可消除区间”数量。

  4. 对每个右端点 ii,先继承 fi1f_{i-1}(不选任何以 ii 结尾的区间),再遍历所有以 ii 为右端的合法起点 uu,计算其对应候选值 fu2+1f_{u-2}+1(若 u=1u=1 则为 11),取最大更新 fif_i

  5. 最大可配对消除区间数为 fn1f_{n-1},剩下必须单点拿的次数即 nfn1n-f_{n-1}

参考代码

#include <bits/stdc++.h>
using namespace std;

const int N=2505;
int a[N],f[N];
vector<int> G[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<n;i++)
{
int t=a[i];
for(int j=i;j<n;j++)
{
if(j>i)t=a[j]-t;
if(t<=0)break;
if(t==a[j+1])G[j].push_back(i);
}
}
for(int i=1;i<n;i++)
{
f[i]=f[i-1];
for(auto u:G[i])
{
f[i]=max(f[i],u==1?1:f[u-2]+1);
}
}
cout<<n-f[n-1]<<'\n';
return 0;
}