Antenna Coverage //dp

17 篇文章 1 订阅

题目链接:http://codeforces.com/contest/1253/problem/E

题意:坐标轴[1,m]上有很多信号基站,每个基站都有一个以其坐标为中心的覆盖范围,覆盖范围关于中心对称,现在让增加一些基站的信号覆盖范围,使[1,m]范围全部覆盖,求最小代价。

思路:第一眼看像贪心,发现假了,如果对基站坐标从小到大排个序,从前往后贪的话会有问题,比如第三个样例就过不去。那只能dp做了。
(1)可以从前往后推,我们加一个基站在0点处,覆盖范围为][0,0],定义dp[i]为从点0覆盖到点i所用最小花费。
那么可以得到状态转移方程:
d p [ i ] = d p [ i − 1 ] dp[i]=dp[i-1] dp[i]=dp[i1](如果点i原本就被覆盖)
点i没有被覆盖就去i前面找基站,假设位置为j,覆盖范围为 [ l , r ] [l,r] [l,r],则:
d p [ i ] = m i n ( d p [ i ] , d p [ m a x ( 0 , l − ( i − r ) − 1 ) ] + ( i − r ) ) dp[i]=min(dp[i],dp[max(0,l-(i-r)-1)]+(i-r)) dp[i]=min(dp[i],dp[max(0,l(ir)1)]+(ir))
dp[m]即为结果

#include <bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<ll,int> P;
#define ft first
#define sd second
const int maxn=1e5+10;
const ll inf=1e15;
int n,m;
struct node{
    int l,r;
}no[maxn];
int dp[maxn];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        int x,s;
        scanf("%d%d",&x,&s);
        no[i].l=max(0,x-s);
        no[i].r=min(m,x+s);
    }
    dp[0]=0;
    for(int i=1;i<=m;i++){
        dp[i]=i;//初始化为最大花费
        for(int j=0;j<n;j++){
            if(no[j].l<=i&&no[j].r>=i)dp[i]=min(dp[i],dp[i-1]);
            else if(no[j].r<i){//去前面找个最优的基站进行扩建
                int u=i-no[j].r;
                dp[i]=min(dp[i],dp[max(no[j].l-u-1,0)]+u);
            }
        }
    }
    cout<<dp[m]<<'\n';
    return 0;
}

(2)也可以从后往前推,我们加一个基站在m+1点处,覆盖范围为][m+1,m+1],定义dp[i]为从点i+1覆盖到点m+1所用最小花费。
那么可以得到状态转移方程:
d p [ i ] = d p [ i + 1 ] dp[i]=dp[i+1] dp[i]=dp[i+1](如果点i+1原本就被覆盖)
点i+1没有被覆盖就去i点后面找基站,假设位置为j,覆盖范围为 [ l , r ] [l,r] [l,r]则:
d p [ i ] = m i n ( d p [ i ] , d p [ m i n ( 0 , r + ( l − i − 1 ) ) ] + ( l − i − 1 ) ) dp[i]=min(dp[i],dp[min(0,r+(l-i-1))]+(l-i-1)) dp[i]=min(dp[i],dp[min(0,r+(li1))]+(li1))
dp[0]即为结果

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<ll,int> P;
#define ft first
#define sd second
const int maxn=1e5+10;
const ll inf=1e15;
int n,m;
struct node{
    int l,r;
}no[maxn];
int dp[maxn];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        int x,s;
        scanf("%d%d",&x,&s);
        no[i].l=max(0,x-s);
        no[i].r=min(m,x+s);
    }
    dp[m]=0;
    for(int i=m-1;i>=0;i--){
        dp[i]=m-i;//初始化为最大花费
        for(int j=0;j<n;j++){
            if(no[j].l<=i+1&&no[j].r>=i+1)dp[i]=min(dp[i],dp[i+1]);
            else if(no[j].l>i){//去后面找个最优的基站进行扩建
                int u=no[j].l-i-1;
                dp[i]=min(dp[i],dp[min(no[j].r+u,m)]+u);
            }
        }
    }
    cout<<dp[0]<<'\n';
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值