1 条题解

  • 0
    @ 2026-10-26 3:05:46

    思路与算法

    把已经发信号的烽火台作为选中的位置。任意连续m个中至少选一个,等价于首段、相邻选中点之间、尾段的未选数量都小于m。

    在位置0放一个代价为0的虚拟起点。定义dp[i]为最后一个选中位置是i时的最小费用,则dp[i]=a[i]+min(dp[j]),其中max(0,i-m)≤j≤i-1。单调队列维护这段窗口的最小dp:删去过期下标,用队首转移,再淘汰队尾不优于新点的候选并加入i。

    在n+1放代价为0的虚拟终点。它的合法前驱范围为max(0,n+1-m)..n,答案是此范围的最小dp。代码不另存终点,只在末尾更新队首有效范围后输出。按照题面给出的独立n、m范围,m>n时没有完整的m长窗口,可以不选任何台,答案0。

    正确性说明

    若有一个m长窗口完全未选,它位于首段、尾段或两次选择之间,就会出现至少m个连续未选位置。反之,任何至少m个连续未选位置都包含一个这样的窗口。因此约束等价于含虚拟端点的相邻选择距离不超过m。

    固定最后选中i,前一次选择j必须在上述范围内;加上i的费用之后,前面使用该j的最优方案即可得到最优dp[i]。转移枚举了所有合法前驱,故状态正确。

    若旧候选j更早且费用不小于一个更新候选,新候选的未来费用不更差且过期更晚,旧候选可安全删除;队首因此始终给出窗口最小值。终点转移保证尾段也符合要求,得到全局最小费用。

    复杂度

    dp与队列数组初始化O(n)。每个位置入队一次、至多出队一次,全部转移和终点检查合计O(n)时间,空间O(n)。

    易错点

    过期条件为j<i-m,等于i-m的候选仍合法。只计算到dp[n]会错误强制最后一台发信号,必须对合法尾段取最小值。m=1时所有台都要选;m=n时至少选一台;m>n时虚拟起点可以直接到终点,代价为0。初始dp[0]=0,其余状态依次计算,不使用未计算的值。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<ll> dp(n + 1);
        vector<int> q(n + 1);
        int head = 0, tail = 1;
        q[0] = 0;
        for (int i = 1; i <= n; ++i) {
            ll cost;
            cin >> cost;
            while (head < tail && q[head] < i - m)
                ++head;
            dp[i] = cost + dp[q[head]];
            while (head < tail && dp[q[tail - 1]] >= dp[i])
                --tail;
            q[tail++] = i;
        }
        while (head < tail && q[head] < n + 1 - m)
            ++head;
        cout << dp[q[head]] << '\n';
    }
    
    • 1

    信息

    ID
    1649
    时间
    1000ms
    内存
    512MiB
    难度
    普及
    标签
    递交数
    1
    已通过
    1
    上传者