添加链接描述
题目大意就是给你一串n为长度由01组成的串,再给你一个k代表路由器的信号范围,串中1代表可以在这里放路由器,0代表不行,使一个点有信号有两种办法
- 点i直接连宽带,花费i的钱(i从1~n)
- 在max(1, i-k) or min(n, i+k)的地方(也就是信号范围内)已经设置了路由器,路由器的信号可以覆盖到这一点,那么这一点就不用花任何钱就有信号。
当然路由器也是要花钱的,花费和连宽带相同,也就是在点i放置路由器, 花费i的钱
现在要你求出让i个点都有信号所花的最少的钱
思路大体是dp, dp[i]表示到第i点所花的最少钱,我是从后往前看(当然也可以从前往后看), 每一点都有两种可能的方法:
3. 最无脑 ,直接连宽带 ,花费i的钱。 dp[i-1] = min(dp[i-1], dp[i] + i)从(0~n-1编号)
4. 若有一个路由器再与他 k 的信号范围内,就设他的坐标为x吧,那么一直到max(0, x-k)的范围内都有信号,那么在max(0, x-k)这个点的最优解就求出来了。贪心的思想就是如果有不止一个路由器可以放在k的范围内,那你肯定选与当前最远(最靠近0)的那个位置放,为什么?因为首先越靠近0他放置一个路由器所花的钱越少, 其次他还能覆盖到更多的范围,所以去最左边切不超过k的那个’1’点放置路由器最优的,这里可以预处理一个pre[]数组来表示这个,用 i - pre[i] <= k来判断能不能通过放路由器的方式来使这点有信号。
int idx = max(0, pre[i-1] - k);
dp[idx] = min(dp[idx], dp[i] + pre[i-1] + 1);
下面是ac代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll maxn = 2 * (int)1e5 + 100;
const ll INF = (ll)1e18;
ll n, k, dp[maxn], pre[maxn];
string s;
inline ll mymax(ll a, ll b) {
if (a > b) return a;
else return b;
}
inline ll mymin(ll a, ll b) {
if (a < b) return a;
else return b;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> k;
cin >> s;
if (k >= n) {
cout << 1 << '\n';
return 0;
}
ll cur = -4 * n;
for (int i = 0; i < n; ++i) {
pre[i] = cur;
}
for (int i = n-1; i >= -k; --i) {
if (i >= 0 && s[i] == '1') {
cur = i;
}
if (i + k < n) {
pre[i + k] = cur;
}
}
for (int i = 0; i < n; ++i) {
dp[i] = INF;
}
dp[n] = 0;
for (int i = n; i > 0; --i) {
if ((i-1) - pre[i-1] <= k) {
int idx = mymax(0, pre[i-1] - k);
dp[idx] = mymin(dp[idx], pre[i-1] + 1 + dp[i]);
}
dp[i-1] = mymin(dp[i-1], dp[i] + i);
}
cout << dp[0] << '\n';
return 0;
}