【CF】1216F-WiFi 题解

传送门:1216F
标签:动态规划

题目大意

你是一名宿舍管理员,在一条直走廊上有n 个连续的房间。房间编号从 1 到 n。你需要将所有n 间房连接到互联网。你可以直接将每个房间连接到互联网,第 i 个房间的费用为i 枚硬币。有些房间有路由器的位置。放置第 i 个房间的路由器的费用也是i 枚硬币。不能在一个没有位置的地方放置路由器。当你在第 i 个房间放置路由器时,你会将号码从max(1,i−k) 到min(n,i+k) 包含在内的所有房间连接到互联网,其中 k 是路由器的范围。所有路由器的 k 值相同。计算将所有n 间房连接到互联网的最低总费用。你可以假设拥有路由器数量大于或等于有路由器位置的房间数量。

输入:第一行包含两个整数 n 和 𝑘——房间的数量和每个路由器的范围。第二行是一个长度为 n 的字符串 s,只包含零和一。如果字符串的第 i 个字符等于 “1”,那么第 i 个房间有一个路由器的位置。如果字符串的第i 个字符等于 “0”,则不能在第 i 个房间放置路由器。

输出:打印一个整数——将所有n 间房连接到互联网的最低总费用。

算法分析

  • 设 dp_i 是连接房间 i 到 n-1 所需的总成本(0 编号)。最初 dp_n = 0,所有其他值都是 +∞。如果我们迭代 n-1 到 0 并做出一些转换,最后答案将是 dp_0。
  • 第一个转换最容易:用 dp_{i+1} + i + 1 更新 dp_i(直接连接当前房间)。为了进行其他转换,我们需要携带两个集合 mins 和 vals,以及一个名为 del 的数组,长度为 n。mins 集合携带 dp_{i+1}, dp_{i+2}, …, dp_{i+k+1} 的所有值。最初它携带 dp_n = 0。vals 集合携带覆盖某些房间尾部并覆盖房间 i 的最小成本。del 数组帮助我们有效地携带 vals 集合。
  • 首先,如果 i+k+2 ≤ n,则从 mins 中删除 dp_{i+k+2}。然后从 vals 中删除 del_i 的所有值。然后如果 s_i = ‘1’,则 val 是 mins 的最小值加上 i + 1。用 val 更新 dp_i 并将 val 插入 vals。此外,如果 i-k-1 ≥ 0,请将 val 添加到 del_{i-k-1}。在对当前 i 完成所需的一切之后,将值 dp_k 添加到 mins 集合。算法的总时间复杂度:O(nlogn)。好像还有O(n)的解决方案,但是能过就是好算法。

代码实现

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5, mod = 1e9 + 7;
int nxt[N];
ll dp[N];
int n,k;
ll cal(int i){
    if(i < 0)
        return 0;
    ll &ret = dp[i];
    if(~ret)
        return ret;
    ret = cal(i-1)+i+1;
    int best = nxt[max(0,i-k)];
    if(best<=i+k){
        ret = min(ret,cal(best-k-1)+best+1);
    }
    return ret;
}
int main() {
    memset(dp,-1,sizeof dp);
    cin >> n >> k;
    string s;
    cin >> s;
    int cur = 1e9;
    for (int i = n-1; i >=0 ; --i) {
        if (s[i] == '1') {
            cur = i;
        }
        nxt[i]=cur;
    }
    cout << cal(n-1);

}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值