School Team Contest #1 (Winter Computer School 2010/11) C. Moon Craters(*)

题目链接
School Team Contest #1 (Winter Computer School 2010/11) C. Moon Craters(*)_第1张图片
题意:给出n个圆心在x轴上的圆的圆心横坐标和半径,要求从其中选出尽可能多的圆使得任意两圆只能是相邻、相切或包含,问最多可以选出多少圆满足条件
思路:题意转化一下,问题转化为给出nn个区间,选取尽可能多的区间使得所选任意两个区间不能相交,把区间端点离散化变成mm个端点,记录每个端点作为左端点时对应的右端点集合,以dp[i][j]dp[i][j]表示ii端点到jj端点可以选的圆的最大数量,对于ii端点可选可不选,不选ii端点则状态变为dp[i+1][j]dp[i+1][j],选ii端点则要枚举其对应的右端点kk,进而状态变成dp[i][k]+dp[k][j]dp[i][k]+dp[k][j],从这些状态中选取最优值赋给dp[i][j]dp[i][j]即可,注意如果i,ji,j两端点形成的区间存在,则答案加一,记忆化搜索求出所有dpdp值,答案即为dp[1][m]

#include
using namespace std;
typedef long long ll;
const int maxn=4005;
int dp[maxn][maxn],vis[maxn][maxn],u[maxn],v[maxn],num[maxn],c[maxn][maxn];
vector<int>g[maxn];
int dfs(int l,int r)
{
	if(l>=r) return dp[l][r]=0;
	if(dp[l][r]!=-1) return dp[l][r];
	dp[l][r]=dfs(l+1,r);//不选
	for(int i:g[l])
	{
		int t=v[i];
		if(t<r)
		{
			if(dp[l][r]<dfs(l,t)+dfs(t,r))
			dp[l][r]=dp[l][t]+dp[t][r],c[l][r]=i;
		}
	 } 
	 return dp[l][r]+=(vis[l][r]>0);
}
void check(int l,int r)
{
	if(l>=r) return ;
	if(vis[l][r]) printf("%d ",vis[l][r]);
	if(c[l][r])
	{
		check(l,v[c[l][r]]);
		check(v[c[l][r]],r);
	}
	else check(l+1,r);
}
int main()
{
	int n,x,t,m=0;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d %d",&x,&t);
		ll l=x-t,r=x+t;
		num[++m]=l,num[++m]=r;
		u[i]=l,v[i]=r;
	}
	sort(num+1,num+1+m);
	int size=unique(num+1,num+1+m)-num-1;
	for(int i=1;i<=n;++i)
	{
		u[i]=lower_bound(num+1,num+1+size,u[i])-num;
		v[i]=lower_bound(num+1,num+1+size,v[i])-num;
		g[u[i]].push_back(i);
		vis[u[i]][v[i]]=i;
	}
	memset(dp,-1,sizeof(dp));
	printf("%d\n",dfs(1,size));
	check(1,size);
}

你可能感兴趣的:(区间DP)