一、题目描述
烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上。一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情。在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价。为了使情报准确的传递,在m个烽火台中至少要有一个发出信号。现输入n、m和每个烽火台发出的信号的代价,请计算总共最少需要花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递。
二、输入输出样例
5 3
1 2 5 6 2
4
三、思路
1.方法——动态规划
这道题要求我们求最小值,数据又很大,我们肯定想到会用动态规划算法解题。根据我们定义线性动态规划的常见操作,可以得到如下定义:定义dp[i]表示点燃第i个烽火台(前面已经点燃)的最小花费。经过对题目的分析后,可以得到动态转移方程:dp[i] = min(dp[j]) + c[i] (i - m <= j <= i - 1, c[i]表示点燃第i个烽火台的花费)。
2.优化——单调队列
但是,问题来了。如果我们用暴力枚举动态转移方程中的j的话,时间复杂度O(nm),超时!怎么办?考虑优化。注意到j是有范围的,我们可以用单调队列进行优化。
单调队列模板(取最大):
#include <bits/stdc++.h>
using namespace std;
struct node
{
int v, k;
node(){}
node(int x, int y)
{
v = x;
k = y;
}
};
int n, m;
int dddl() //单调队列
{
node q[300002];
int f = 1, l = 1;
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
while(f < l && i - q[f].k >= m)
f++;
while(f < l && dp[i] >= q[l - 1].v)
l--;
q[l] = node(x, i);
l++;
}
return q[f].v;
}
int main()
{
scanf("%d %d", &n, &m);
dddl();
return 0;
}
3.注意事项
答案是min(dp[i]) (n - m + 1 <= i <= n),所以最后要再遍历一遍dp数组的最后m位。
四、最终的标程
#include <bits/stdc++.h>
using namespace std;
struct node
{
int v, k;
node(){}
node(int x, int y)
{
v = x;
k = y;
}
};
node q[300002];
int dp[300002];
int n, m, f = 1, l = 1;
int main()
{
scanf("%d %d", &n, &m);
q[l] = node(0, 0);
l++;
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
while(f < l && i - q[f].k > m)
f++;
dp[i] = q[f].v + x;
while(f < l && dp[i] <= q[l - 1].v)
l--;
q[l] = node(dp[i], i);
l++;
}
int ans = 0x7fffffff;
for(int i = n - m + 1; i <= n; i++)
ans = min(ans, dp[i]);
printf("%d", ans);
return 0;
}