思路见注释;
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
",f[m]);
return 0;
}