信号
信号
有 个房子排成一排,从左到右依次编号为 。
其中一些房子内装有无线信号发射器。
这些信号发射器的有效覆盖半径为 。
更准确地说,如果第 号房子内装有信号发射器,则所有房间编号在 范围内的房子均可被其发出的无线信号覆盖,而其余房子则不会被其发出的无线信号覆盖。
例如,假设 ,且第 、 号房子内装有信号发射器,则第 号房子内的发射器发出的信号可以覆盖第 号房子,第 号房子内的发射器发出的信号可以覆盖第 号房子,将两个发射器全部打开,则无线信号可以覆盖到所有房子。
初始时,所有无线信号发射器都是关闭的,请计算至少开启多少个无线信号发射器,才能保证所有房子都被无线信号覆盖到。
输入格式
第一行包含两个整数 和 。
第二行包含 个整数 ,每个数要么是 ,要么是 , 表示第 号房子内装有无线信号发射器, 表示第 号房子内未装无线信号发射器。
输出格式
一个整数,表示需要开启的无线信号发射器的最少数量。
如果无法使得所有房子都被无线信号覆盖到,则输出 。
数据范围
前 个测试点满足 。
所有测试点满足 ,。
输入样例1:
6 2 0 1 1 0 0 1
输出样例1:
3
输入样例2:
5 3 1 0 0 0 1
输出样例2:
2
输入样例3:
5 10 0 0 0 0 0
输出样例3:
-1
输入样例4:
10 3 0 0 1 1 0 1 0 0 0 1
输出样例4:
3
解题思路
假设有两个信号发射器,一个比较靠前,一个比较靠后,并且两个信号发生器都可以覆盖到最左边的那个房子,即第个房子。
我们考虑一下应该选择哪个发生器。可以把所有的方案(最终答案的方案)分成两类,第一类是选择靠前的发生器的方案,第二类是选择靠后的发生器的方案。我们任取第一类中的方案,且这个方案可以覆盖所有的房子,我们把靠左的那个发生器换成靠右的发生器,仍然是一个合法的方案:
而且需要的发生器的数量是相同的,所以对于第一类中的方案,我们都可以在第二类中找到一个方案,使得这个方案所需要的发生器不比第一类的方案多。所以第二类中的方案都不比第一类的方案差,因此第二类方案中必然存在最优解(第一类方案也可能存在最优解),所以我们只需要考虑第二类的方案就可以了,即应该选择靠后的那个发生器。
以此类推,在能够覆盖到最左边那个还没被覆盖的房子的条件下,每次都应该选择尽可能靠后的房子。
这题贪心的模型可以参考这个模板题:区间覆盖。
AC代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 const int N = 1010; 6 7 int a[N], sz; 8 9 int main() { 10 int n, m; 11 scanf("%d %d", &n, &m); 12 for (int i = 1; i <= n; i++) { 13 int val; 14 scanf("%d", &val); 15 if (val) a[sz++] = i; // 记录有发生器的房间 16 } 17 18 int ret = 0, last = 0; // last表示上一个被覆盖的房间,因此还没被覆盖的房间是last+1 19 for (int i = 0; i < sz; i++) { 20 if (last >= n) break; // 所有的房间已被覆盖 21 // 第a[i]个房间最左边能够覆盖到的房间为a[i]-m+1,如果a[i]-m+1 > last+1,表明不能够覆盖到上一个未覆盖到的房间 22 if (a[i] - m > last) break; 23 24 int j = i; // 在能够覆盖last+1这个房间的前提下,寻找最靠右的房间 25 while (j + 1 < sz && a[j + 1] - m <= last) { 26 j++; 27 } 28 last = a[j] + m - 1; // 更新last 29 i = j; 30 ret++; 31 } 32 33 printf("%d", last >= n ? ret : -1); 34 35 return 0; 36 }
这题还可以用动态规划来做。
AC代码如下,时间复杂度是:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int N = 1010; 7 8 int a[N], f[N]; 9 10 int main() { 11 int n, m; 12 scanf("%d %d", &n, &m); 13 for (int i = 1; i <= n; i++) { 14 scanf("%d", a + i); 15 } 16 17 memset(f, 0x3f, sizeof(f)); 18 for (int i = 1; i <= m; i++) { // 初始化一开始能够覆盖到第1个房间的发射器 19 if (a[i]) f[i] = 1; 20 } 21 for (int i = 1; i <= n; i++) { 22 if (a[i]) { 23 for (int j = max(1, i - 2 * m + 1); j < i; j++) { // 注意j可能会小于0越界 24 if (a[j]) f[i] = min(f[i], f[j] + 1); 25 } 26 } 27 } 28 29 int ret = N; 30 for (int i = max(1, n - m + 1); i <= n; i++) { // 枚举能够覆盖到第n个房间的最后一个发射器 31 if (a[i]) ret = min(ret, f[i]); 32 } 33 printf("%d", ret == N ? -1 : ret); 34 35 return 0; 36 }
可以用单调队列优化到,AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1010; 5 6 int a[N], f[N]; 7 int q[N], hh, tt = -1; 8 9 int main() { 10 int n, m; 11 scanf("%d %d", &n, &m); 12 for (int i = 1; i <= n; i++) { 13 scanf("%d", a + i); 14 } 15 16 memset(f, 0x3f, sizeof(f)); 17 for (int i = 1; i <= m; i++) { 18 if (a[i]) f[i] = 1; 19 } 20 for (int i = 1; i <= n; i++) { 21 if (a[i]) { 22 while (hh <= tt && q[hh] + m - 1 < i - m) { 23 hh++; 24 } 25 if (hh <= tt) f[i] = min(f[i], f[q[hh]] + 1); 26 while (hh <= tt && f[q[tt]] >= f[i]) { 27 tt--; 28 } 29 q[++tt] = i; 30 } 31 } 32 33 int ret = n + 1; 34 for (int i = max(1, n - m + 1); i <= n; i++) { 35 if (a[i]) ret = min(ret, f[i]); 36 } 37 printf("%d", ret == n + 1 ? -1 : ret); 38 39 return 0; 40 }
参考资料
AcWing 4421. 信号(AcWing杯 - 周赛):https://www.acwing.com/video/3871/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16275993.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效