CF607A Chain Reaction【dp+二分】

原题链接

题意

给定 \(n\) 个激光塔,每个激光塔有一个坐标 \(a_i\) 和一个威力 \(b_i\),当第 \(i\) 个激光塔被激活后,坐标 \(\geq a_i-b_i\) 的激光塔将被摧毁。现在在所有激光塔的右侧放置一个坐标和威力任意的激光塔,从右到左依次激活没有被摧毁的激光塔,求最少要摧毁多少个激光塔。

思路

首先注意,虽然样例中的 \(a_i\) 都是升序给出的,但是题目中并未保证 \(a_i\) 升序,所以在最开始要进行排序

定义 \(f[i]\) 表示以第 \(i\) 个激光塔为起点,依次向右激活没被摧毁的激光塔,摧毁的激光塔数量。注意这里不能定义为最小值,因为本题中的起点一旦确定,那么摧毁的数量也就确定了。

注意到题目中给出的最右侧的激光塔的威力任意,其实就可以转化为摧毁第 \(i+1\) 个到第 \(n\) 个激光塔后,再以第 \(i\) 个激光塔为起点向右激活。那么最终的答案就是 \(\min(f[i]+n-i)\)。

接下来考虑状态转移方程。注意到,第 \(i\) 个激光塔被激活后,下一个被激活的肯定是第一个坐标小于 \(a_i-b_i\) 的激光塔,同时因为坐标是单调递增的,就可以用二分来查找这个激光塔 \(j\)。那么状态转移方程就是 \(f[i]=f[j]+i-j-1\)。因为在 \(j\) 和 \(i\) 之间的激光塔显然被摧毁了。

code:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
struct NLC{
	int a,b;
    bool operator <(const NLC &t)const{
	    return a<t.a;
	}
}p[N];
int ans=INF,n,f[N];
int find(int x)
{
	int l=1,r=n;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(p[mid].a<x) l=mid;
		else r=mid-1;
	}
	return p[l].a<x?l:0;
}
int min(int a,int b){return a<b?a:b;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&p[i].a,&p[i].b);
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++)
	{
		int j=find(p[i].a-p[i].b);
		f[i]=f[j]+i-j-1;
		ans=min(ans,f[i]+n-i);
	}
	printf("%d\n",ans);
	return 0;
}
上一篇:PHP利用 JSON 将XML转换为数组


下一篇:javaweb基础笔记(3)