Codeforces 559E

博客探讨了一道名为Codeforces 559E的题目,通过反向思考将问题转化为判断区间覆盖可能性。通过离散化将问题规模减小,并使用动态规划(DP)预处理每个区间是否可覆盖。具体策略是固定左端点,枚举右端点,维护一个二维DP数组来记录当前状态下的最远覆盖范围。最终以O(n^3)的时间复杂度得出解决方案。文章提到存在更优的贪心策略,但作者并未深入探讨。
摘要由CSDN通过智能技术生成

比较简单的题。
如果尝试直接DP每个灯可能比较困难,不妨反过来想,考虑给定若干个不相交区间,判定是否能完全覆盖。离散化后只有 O ( n 2 ) \mathcal O(n^2) O(n2)个有效的区间,用一个简单的DP就可以预处理出每个区间是否可以完全覆盖了,即我们固定左端点 s s s,枚举右端点,维护 F [ i ] [ j ] F[i][j] F[i][j]表示当前考虑到的右端点为 i i i,只用了 [ s , i ] [s,i] [s,i]中的灯, [ s , i ] [s,i] [s,i]中还没有被覆盖的最左边位置为 j j j所能覆盖的最远位置,转移很简单。
最后再跑个DP求出答案就好了,时间复杂度 O ( n 3 ) \mathcal O(n^3) O(n3)。似乎有神仙贪心,想不动了。

#include <bits/stdc++.h>
#define FR first
#define SE second
 
using namespace std;
 
typedef pair<int,int> pr;
 
inline void update(int &x,int y) {
  x=max(x,y);
}
 
int f[305],val[305];
pr num[305];
 
bool can[305];
int g[305][305],id[305];
 
void dp1(int n,int s) {
  memset(g,255,sizeof(g));
  memset(can,0,sizeof(can));
  g[s-1][s-1]=s-1;
  for(int i=s;i<=n;i++) {
  	if (id[i]) {
  		int l=num[id[i]].FR,r=num[id[i]].SE;
  		for(int j=s-1;j<i;j++)
  		  if (g[i-1][j]!=-1) {
  		  	  update(g[i][(l<=j)?s-1:j],max(g[i-1][j],i));
  		  	  update(g[i][(j==s-1&&g[i-1][j]<i)?i-1:j],max(g[i-1][j],r));
			}
	  }
	else {
		for(int j=s-1;j<i;j++)
		  if (g[i-1][j]!=-1) update(g[i][(j==s-1&&g[i-1][j]<i)?i-1:j],g[i-1][j]);
	}
	if (g[i][s-1]>=i) can[i]=1; 
  }
}
 
void dp2(int n) {
  for(int i=n;i>0;i--) {
  	f[i]=f[i+1];
  	dp1(n,i);
  	for(int j=i+1;j<=n;j++)
  	  if (can[j]) update(f[i],f[j+1]+val[j]-val[i]);
  }
}
 
int main() {
  int n;
  scanf("%d",&n);
  int cnt=0;
  for(int i=1;i<=n;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	num[i]=pr(x,y);
  	val[++cnt]=x-y;
  	val[++cnt]=x;
  	val[++cnt]=x+y;
  }
  sort(val+1,val+cnt+1);
  cnt=unique(val+1,val+cnt+1)-val-1;
  for(int i=1;i<=n;i++) {
  	int u=lower_bound(val+1,val+cnt+1,num[i].FR)-val;
  	int t1=lower_bound(val+1,val+cnt+1,num[i].FR-num[i].SE)-val;
  	int t2=lower_bound(val+1,val+cnt+1,num[i].FR+num[i].SE)-val;
  	id[u]=i;
  	num[i]=pr(t1,t2);
  }
  dp2(cnt);
  printf("%d\n",f[1]);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值