Codeforces Round #600 (Div. 2) E. Antenna Coverage (DP)

传送门

思路见注释;

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef pair<LL,LL> pLL;
const int N=2e5+5;
const double inf=0x3f3f3f3f;
#define ls (i<<1)
#define rs (i<<1|1)
#define fi first
#define se second
#define mk make_pair
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define debug puts("...")
LL read()
{
    LL x=0,t=1;
    char ch=getchar();
    while(!isdigit(ch)){ if(ch=='-')t=-1; ch=getchar(); }
    while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
    return x*t;
}
//f[i] 为 [1,i] 被覆盖的最小花费
int f[N],x[N],s[N];
int main()
{
    int n=read(),m=read();
    for(int i=0;i<=m;i++) f[i]=m;//把[1,i]看作[1,m]的子问题,覆盖[1,m] 最多花费 m,那么其子问题 覆盖[1,i] 最多需要 i;
    for(int i=1;i<=n;i++)
    {
        x[i]=read();
        s[i]=read();
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
            if(x[j]<=i) f[i]=min(f[i],f[max(x[j]*2-i-1,0)]+max(i-x[j]-s[j],0));//第一种情况,让 x[j] 向后扩张覆盖 i 则需要至少 i-x[j]-s[j]的花费(扩张的花费,其可能为负,可能已经被覆盖了),x[j]是向两边同时扩张的,所以 算出 i关于 x[j] 对称的点的前一个位置
            else f[i]=min(f[i],f[max(x[j]-s[j]-1,0)] );//第二种情况,是否可能被后面的点直接覆盖掉(不扩张),在这里不考虑 后面的点扩张之后把前面的i覆盖的情况,因为 x[j] 向前扩张至i,同时也会向后扩张至 i 关于 x[j] 的对称点k, 对于k而言 的第一种种情况与 i的第二种情况等价(都是覆盖后,通过 f[i-1] 转移),所以没有必要,而且也避免了后效性。
    }
    printf("%d\n",f[m]);
    return 0;
}
 
posted @ 2019-12-11 21:14  DeepJay  阅读(99)  评论(0编辑  收藏  举报