题解:洛谷 P1638 逛画展

【题目来源】

洛谷:P1638 逛画展 - 洛谷

【题目描述】

博览馆正在展出由世上最佳的 \(m\) 位画家所画的图画。

游客在购买门票时必须说明两个数字,\(a\)\(b\),代表他要看展览中的第 \(a\) 幅至第 \(b\) 幅画(包含 \(a,b\))之间的所有图画,而门票的价钱就是一张图画一元。

Sept 希望入场后可以看到所有名师的图画。当然,他想最小化购买门票的价格。

请求出他购买门票时应选择的 \(a,b\),数据保证一定有解。

若存在多组解,输出 \(a\) 最小的那组

【输入】

第一行两个整数 \(n,m\),分别表示博览馆内的图画总数及这些图画是由多少位名师的画所绘画的。

第二行包含 \(n\) 个整数 \(a_i\),代表画第 \(i\) 幅画的名师的编号。

【输出】

一行两个整数 \(a,b\)

【输入样例】

12 5
2 5 3 1 3 2 4 1 1 5 4 3

【输出样例】

2 7

【算法标签】

《洛谷 P1638 逛画展》 #二分# #单调队列# #队列# #双指针,two-pointer# #USACO#

【代码详解】

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

const int MAX_N = 1000005;  // 定义数组最大长度
const int MAX_M = 2005;     // 定义颜色种类最大数量
int n, m;                   // n: 珠子数量, m: 颜色种类数
int a[MAX_N];               // 存储珠子颜色序列
int cnt[MAX_M];             // 记录当前窗口中各颜色出现次数
int num;                    // 记录当前窗口中不同颜色数量
int len = 1e9;              // 记录满足条件的最短长度(初始设为极大值)
int l, r;                   // 记录最优解的左右边界

int main()
{
    // 输入珠子数量和颜色种类数
    cin >> n >> m;
    
    // 输入珠子颜色序列
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    // 初始化滑动窗口指针和计数器
    int i = 1, j = 1;       // i: 窗口左边界, j: 窗口右边界
    cnt[a[1]] = 1;          // 初始化第一个珠子的颜色计数
    num = 1;                // 当前窗口包含1种颜色
    
    // 滑动窗口法寻找最短包含所有颜色的子串
    while (j <= n)
    {
        // 如果当前颜色种类不足,扩大右边界
        if (num < m)
        {
            j++;            // 右边界右移
            cnt[a[j]]++;    // 新颜色计数增加
            
            // 如果是新出现的颜色,增加颜色计数
            if (cnt[a[j]] == 1)
                num++;
        }
        
        // 如果当前窗口包含所有颜色
        if (num == m)
        {
            // 更新最优解
            if (len > j - i + 1)
            {
                len = j - i + 1;  // 更新最短长度
                l = i;            // 记录左边界
                r = j;            // 记录右边界
            }
            
            // 尝试缩小左边界
            cnt[a[i]]--;      // 左边界的颜色计数减少
            
            // 如果某种颜色数量减为0,减少颜色计数
            if (cnt[a[i]] == 0)
                num--;
            
            i++;              // 左边界右移
        }
    }
    
    // 输出最优解的左右边界
    cout << l << " " << r;
    
    return 0;
}
// 使用acwing模板二刷
#include <bits/stdc++.h>
using namespace std;

const int N = 1000006;      // 定义数组最大长度
const int COLOR_MAX = 2005;  // 定义颜色种类最大数量
int n, m;                   // n: 珠子数量, m: 颜色种类数
int a[N];                   // 存储珠子颜色序列
int cnt[COLOR_MAX];         // 记录当前窗口中各颜色出现次数
int num;                    // 记录当前窗口中不同颜色数量
int len = 1e9;              // 记录满足条件的最短长度(初始设为极大值)
int l, r;                   // 记录最优解的左右边界

int main()
{
    // 输入珠子数量和颜色种类数
    cin >> n >> m;
    
    // 输入珠子颜色序列
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    // 初始化滑动窗口和计数器
    cnt[a[1]] = 1;          // 第一个珠子的颜色计数
    num = 1;                // 当前窗口包含1种颜色
    
    // 滑动窗口法寻找最短包含所有颜色的子串
    for (int i = 1, j = 1; j <= n; i++)  // i:左边界, j:右边界
    {
        // 当颜色种类不足时,扩大右边界
        while (num < m && j <= n)
        {
            j++;            // 右边界右移
            cnt[a[j]]++;    // 新颜色计数增加
            
            // 如果是新出现的颜色,增加颜色计数
            if (cnt[a[j]] == 1)
                num++;
        }
        
        // 如果当前窗口包含所有颜色
        if (num == m)
        {
            // 更新最优解
            if (len > j - i + 1)
            {
                len = j - i + 1;  // 更新最短长度
                l = i;            // 记录左边界
                r = j;            // 记录右边界
            }
            
            // 尝试缩小左边界
            cnt[a[i]]--;      // 左边界的颜色计数减少
            
            // 如果某种颜色数量减为0,减少颜色计数
            if (cnt[a[i]] == 0)
                num--;
        }
    }
    
    // 输出最优解的左右边界
    cout << l << " " << r;
    
    return 0;
}

【运行结果】

12 5
2 5 3 1 3 2 4 1 1 5 4 3
2 7
posted @ 2026-02-18 21:49  团爸讲算法  阅读(0)  评论(0)    收藏  举报