洛谷 P11838 分析(别看)
2026-10-08 19:41:12
发布于:天津
1 题目
1.1 题目所需求出内容
对于本题,最终要求:能否达到最终的结果
1.2 题目背景、允许、禁止与限制
背景:有一个由 个数组成的被期望输出的数列
允许:
- 使用不超过 个
print语句打印某个数,一个print语句只可以打印一个数字 - 还可以使用
repeat语句,它的使用是无限次的,repeat可以重复print若干次,也就是说如果想输出 只需要 次print
限制:最终要求求出能否使用不超过 个print语句打印出期望序列
1.3 题目数据范围与猜测
1.4 一句话概括题意
有一个被期望输出的数列,你需要判断是否可以使用无数次的repeat语句和至多 次的print语句输出这个数列
2 题目破题推导
2.1 第一步:分情况考虑
我们考虑两种不同的情况
- 普通串(如: ),这种串是无法用
repeat语句解决的,因此只能选择最小的输出数量进行输出 - 可以循环的串(如: ),这种串可以用
repeat语句解决(相当于有循环节 ),因此尝试选择循环节数量最少的进行循环(如为了输出 ,应该选择循环节数量为 而非循环节数量为 )
那接下来我们想一下如何判断这个循环节是否存在呢?
2.2 第二步:数学
要想知道循环节是否存在,有三个东西必须要知道:分别是
- 序列(题目中已经给出)
- 需要检查序列中某一区间的左右端点
- 循环节可能的长度
知道这三个之后,就要有两个条件
- 首先,需要保证区间长度是 的倍数,也就是说
- 其次,需要保证可能循环节中的每一项都和第一个循环节相对应的“相对位置”上的元素相同
也就是 时刻
举个例子:序列 检测 时就应保证
3 模型匹配
先提取关键词:时刻求取当前是否可行、区间
是
4 具体方案
4.1 动态规划定义(重点)
首先,先建立 dp 数组
设置 打印区间 范围内所需的最小print语句数量
4.2 初始化
都是
但是一格一定需要用一个print语句,因此对于每个 ,都要将 设置为
4.3 状态转移方程
分两部分
-
先判断能否用
repeat语句(特殊情况)的:
枚举循环节长度 从 ,每次检查 或者 是否为循环节,不是则进入普通情况,否则可以记录 为所有可能循环节长度 的 的最小值 -
一个是普通情况的:
枚举区间 之间的中间点 , 就是所有 中的最小值(大区间操作次数 最小左区间操作次数+最小右区间操作次数)
4.4 答案求取
检查 是否成立
5 最终代码(禁止抄袭,仅用于参考)
#include <bits/stdc++.h>
using namespace std;
const int N = 111;
int t;
int a[N], dp[N][N];
bool check(int l, int r, int k){
if ((r - l + 1) % k != 0){
return false;
}
for (int i = l;i <= r;i++){
int pos = l + ((i - l) % k);
if (a[i] != a[pos]){
return false;
}
}
return true;
}
int main(){
cin >> t;
while(t--){
int n, k;
memset(dp, 0x3f, sizeof(dp));
cin >> n >> k;
for (int i = 1;i <= n;i++){
cin >> a[i];
dp[i][i] = 1;
}
for (int len = 2;len <= n;len++){
for (int l = 1;l + len - 1 <= n;l++){
int r = l + len - 1;
for (int m = 1;m * m <= len;m++){
if (len % m == 0){
if (check(l, r, m)){
dp[l][r] = min(dp[l][r], dp[l][l + m - 1]);
}
if (check(l, r, len / m)){
dp[l][r] = min(dp[l][r], dp[l][l + len / m - 1]);
}
}
}
for (int k = l;k <= r;k++){
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]);
}
}
}
cout << (dp[1][n] <= k ? "YES" : "NO") << endl;
}
return 0;
}
这里空空如也















有帮助,赞一个