题目链接:https://codeforces.com/problemset/problem/1216/F

题目大意:有n个房间,1代表这个房间可以安装路由器(代价为这个房间的编号i),且路由器覆盖范围为左侧到max(1, i - k),  右侧到max(n, i + k);0代表这个房间不能安装路由器,需要直接连接上网(代价为这个房间的编号i),或者可以蹭安装了路由器房间的网(没有代价)。问让这n个房间能全部连上网的最小代价是多少。

用线段树维护的dp

设dp[i][0/1]表示前i - 1个位置已经完成了覆盖,第i个位置 不放(0)/放(1) 路由器时,所需花费的最小代价。

考虑状态转移如下:

假设前i - 1个房间已经用最小代价全部连接上网,

  1. 无论这个房间是属性是0还是1,都有:dp[i][0] = min{dp[i - 1][0] + i, query(前面放路由器, 1, 1, n, i - k, i - 1)};

这表示,第i号房间不放路由器的话,前i个房间全部连接上网的代价 = min{前i - 1个房间全部连接上网的最小代价 + 第i个房间直接连接上网的代价i, 蹭前面放置路由器房间的网(此时第i个房间没有花费任何代价,转移到这的时候代价会和它前面放路由器房间的代价相同)}

  2.  对于可以放置路由器的房间,有:dp[i][1] = min{query(前面放路由器, 1, 1, n, i - k, i - 1), query(前面不放路由器, 1, 1, b, i - k - 1, i - 1)} + i;

这表示,第i个房间选择放路由器的话,前i个房间全部连接上网所需要的最小代价 = min{[i - K - 1, i - 1]区间内的使用路由器的最小值, 前面的[i - K, i - 1]区间的不使用路由器的最小值} + 这个房间放置路由器的代价i

 1 #include<iostream>
 2 #include<vector>
 3 #include<map>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstdio>
 7 #include<cmath>
 8 #include<string>
 9 #include<set>
10 #include<complex>
11 #include<cstdio>
12 #include<cstring>
13 #include<stack>
14 #include<iomanip>
15 #include<bitset>
16 #include<typeinfo>
17 #include<random>
18 #include<unordered_map>
19 #include<unordered_set>
20 using namespace std;
21
22 const int maxn = 2e5 + 10;
23 const long long inf = 0x3f3f3f3f3f3f3f3f;
24
25 int n, k;
26 char str[maxn];
27 long long dp[maxn][5];
28
29 long long tree_no[maxn * 4];
30 long long tree_yes[maxn * 4];
31
32 void push_up(long long tmp[], int rt){
33     int ls = rt * 2, rs = rt * 2 + 1;
34     tmp[rt] = min(tmp[ls], tmp[rs]);
35 }
36
37 void build(long long tmp[], int rt, int l, int r){
38     tmp[rt] = inf;
39     if(l == r){
40         return;
41     }
42     int mid = (l + r) / 2;
43     build(tmp, rt * 2, l, mid);
44     build(tmp, rt * 2 + 1, mid + 1, r);
45     // push_up(tmp, rt);
46 }
47
48 long long query(long long tmp[], int rt, int l, int r, int ql, int qr){
49     if(ql <= l && r <= qr) return tmp[rt];
50     int mid = (l + r) / 2;
51     if(qr <= mid) return query(tmp, rt * 2, l, mid, ql, qr);
52     else if(ql > mid) return query(tmp, rt * 2 + 1, mid + 1, r, ql, qr);
53     else{
54         long long ans_ls = query(tmp, rt * 2, l, mid, ql, qr);
55         long long ans_rs = query(tmp, rt * 2 + 1, mid + 1, r, ql, qr);
56         return min(ans_ls, ans_rs);
57     }
58 }
59
60 void update(long long tmp[], int rt, int l, int r, int p, long long x){
61     if(l == r){
62         tmp[rt] = x;
63         return;
64     }
65     int mid = (l + r) / 2;
66     if(p <= mid) update(tmp, rt * 2, l, mid, p, x);
67     else update(tmp, rt * 2 + 1, mid + 1, r, p, x);
68     push_up(tmp, rt);
69 }
70
71 int main(){
72     scanf("%d %d", &n, &k);
73     scanf("%s", str + 1);
74     build(tree_no, 1, 1, n);
75     build(tree_yes, 1, 1, n);
76     dp[n][1] = inf; dp[0][0] = 0;
77     for(int i = 1; i <= n; i++){
78         if(i > 1) dp[i][0] = min(dp[i - 1][0] + i, query(tree_yes, 1, 1, n, i - k, i - 1));
79         else dp[i][0] = i;
80         update(tree_no, 1, 1, n, i, dp[i][0]);
81         if(str[i] == '1'){
82             if(i - k <= 1) dp[i][1] = i;
83             else if(i > 1) dp[i][1] = min(query(tree_yes, 1, 1, n, i - k, i - 1),
84                                           query(tree_no, 1, 1, n, i - k - 1, i - 1)) + i;
85             update(tree_yes, 1, 1, n, i, dp[i][1]);
86         }
87     }
88     printf("%lld\n", min(dp[n][1], dp[n][0]));
89     return 0;
90 }
View Code

参考博客:https://blog.csdn.net/qq_41730082/article/details/101119577

01-22 12:07