其他分享
首页 > 其他分享> > AcWing 1089 . 烽火传递

AcWing 1089 . 烽火传递

作者:互联网

题目传送门

i结尾的f[i],滑动窗口的区间是[i - m,i - 1],单调队列维护的是该区间的最小值,由于滑动窗口不包含i,因此f[i]需要在while上方进行更新

一、简单DP

#include <bits/stdc++.h>

using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200010;
int f[N];
int w[N], n, m;

int main() {
    /**
    普通DP大法
    状态表示:
     ①集合:f[i]表示前i个灯塔满足条件时,并且i点亮。
     ②属性:最小代价
    状态计算:f[i]=min(f[j]+w[i]) (i−m≤j≤i−1)
    答案:(n−m+1)~n必须有灯塔亮,所以枚举一下求出最小值即可
    */
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> w[i];

    //初始化
    memset(f, 0x3f, sizeof f);//预求最小,先设最大
    f[0] = 0;//边界点

    for (int i = 1; i <= n; i++)//遍历每一个烽火台
        for (int j = max(0, i - m); j <= i - 1; j++)//向前查看它之前的m个
            f[i] = min(f[i], f[j] + w[i]);

    int res = INF;
    //A城市0号,B城市:n+1号
    //极限思考法的话,可以设m=2,通过画图,就可以推出就是n+1-m为可能的位置
    //这是一个常用的套路:从最终可能合理的位置找一遍!
    //注意这个起点,表示如果最后一个位置是n+1-m的话,也是合理解,可以覆盖掉第n个
    for (int i = n + 1 - m; i <= n; i++) res = min(res, f[i]);
    cout << res << endl;
    return 0;
}

二、滑动窗口优化的DP

#include <bits/stdc++.h>

using namespace std;

const int N = 200010;
const int INF = 0x3f3f3f3f;
int f[N];
int w[N], n, m;
int q[N];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> w[i];
    int hh = 0, tt = 0;
    //假设城市A算作一个编号为0且代价为0的灯塔
    for (int i = 1; i <= n; i++) { //遍历每一个烽火台
        if (tt >= hh && i - q[hh] > m) hh++;
        //为什么要先更新?
        f[i] = f[q[hh]] + w[i];
        //踢出
        while (tt >= hh && f[q[tt]] >= f[i])tt--;
        q[++tt] = i;
    }
    //同样城市也算一个代价为0的灯塔,城市前m的灯塔至少有个点亮
    int res = INF;
    for (int i = n + 1 - m; i <= n; i++)
        res = min(res, f[i]);
    cout << res << endl;
    return 0;
}

标签:const,int,tt,1089,hh,DP,灯塔,烽火,AcWing
来源: https://www.cnblogs.com/littlehb/p/15816685.html