题意:给定n和k,n表示n个房间,k表示wifi的传播范围,同时给定一个字符串,表示可以放置路由器的房间,如果可以放置路由器,那么max(1, n - k)
和min(n, n + k)范围里的房间都可以得到信号,从而可以不直接联网,每个房间联网的代价为它的下标i。
分析:
我们假设f[i][0 / 1]表示第i号房间不联网/联网的最小代价。
如果联网了,并且这个房间里面由wifi,那就更好,可以从更前面的状态转移过来,否则就从前面一个房间转移过来。
如果f[i][0] (表示第i个房间不联网),显然它需要从前面有wifi的地方转移过来,
如果f[i][1] (表示第i个房间联网),那么它有wifi的话,可以从更前面转移过来,如果没有wifi,那就从前面一个转移过来。
但是这样做的时间复杂度为o(n * k)
不优化的代码,TLE在23个点
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200005;
char s[N];
int f[N][2];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
scanf("%s", s + 1);
memset(f, 0x3f, sizeof f);
f[0][0] = 0, f[0][1] = 0;
for (int i = 1; i <= n; ++i)
{
if (s[i] == '0')
f[i][1] = min(f[i][1], min(f[i - 1][0], f[i - 1][1]) + i);
for (int j = 0; j <= i - 1; ++j)
{
if (s[j] == '1' && j + k >= i)
f[i][0] = min(f[i][0], f[j][1]);
if (s[i] == '1' && j >= i - k - 1)
f[i][1] = min(f[i][1], min(f[j][0], f[j][1]) + i);
}
}
printf("%d
", min(f[n][1], f[n][0]));
return 0;
}
通过线段树维护区间最小值的代码
这里用了三棵线段树,因为我需要从前面有wifi并且状态是联网的房间转移过来,所以不得已再维护一个线段树。
因为我的状态定义和其它博客的不一样,其它博客表示f[i][0/1]这个房间有没有路由,但是这样我觉的分不清楚。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
using LL = long long;
const int N = 200005;
const LL inf = 1e18;
char s[N];
LL f[N][2];
//0:不买 1:买
struct ST
{
int l, r;
LL val;
};
//tr维护不买的,tr2维护买的
ST tr[N * 4], tr2[N * 4], tr3[N * 4];//三棵线段树
void pushup(ST tr[], int u)
{
tr[u].val = min(tr[u].val, tr[u << 1].val);
tr[u].val = min(tr[u].val, tr[u << 1 | 1].val);
}
void build(ST tr[], int u, int l, int r)
{
tr[u].l = l, tr[u].r = r, tr[u].val = inf;
if (l == r)
{
//tr[u].val = inf;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
build(tr, u << 1, l, mid), build(tr, u << 1 | 1, mid + 1, r);
//pushup(tr, u);
}
//单点修改
void modify(ST tr[], int u, int pos, LL v)
{
if (tr[u].l == tr[u].r)
{
tr[u].val = v;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (pos <= mid) modify(tr, u << 1, pos, v);
else if (pos > mid) modify(tr, u << 1 | 1, pos, v);
pushup(tr, u);
}
//区间查询最小值
LL query(ST tr[], int u, int l, int r)
{
if (l <= tr[u].l && r >= tr[u].r)
{
return tr[u].val;
}
int mid = tr[u].l + tr[u].r >> 1;
LL res = inf;
if (l <= mid) res = min(res, query(tr, u << 1, l, r));
if (r > mid) res = min(res, query(tr, u << 1 | 1, l, r));
return res;
}
int main()
{
int n, k;
scanf("%d%d", &n, &k);
scanf("%s", s + 1);
build(tr, 1, 0, n);
build(tr2, 1, 0, n);
build(tr3, 1, 0, n);
for (int i = 1; i <= n; ++i)
f[i][0] = f[i][1] = inf;
modify(tr, 1, 0, 0);//f[i][0]
modify(tr2, 1, 0, 0);//f[i][1]
f[0][0] = 0, f[0][1] = 0;
for (int i = 1; i <= n; ++i)
{
//没有路由,并且购买了的
if (s[i] == '0')
{
LL q = min(f[i - 1][0], f[i - 1][1]);
if (f[i][1] > q + i)
{
f[i][1] = q + i;
modify(tr2, 1, i, f[i][1]);
}
}
//没有购买,由前面的wifi提供
LL res = query(tr3, 1, max(1, i - k), i - 1);
if (f[i][0] > res)
{
f[i][0] = res;
modify(tr, 1, i, f[i][0]);
}
res = inf;
res = min(res, query(tr, 1, max(0, i - k - 1), i - 1));
res = min(res, query(tr2, 1, max(0, i - k - 1), i - 1));
if (s[i] == '1')
{
f[i][1] = res + i;
modify(tr2, 1, i, f[i][1]);
modify(tr3, 1, i, f[i][1]);
}
}
printf("%lld
", min(f[n][1], f[n][0]));
return 0;
}