CF39C Solution

题目链接

题目翻译

给出直线上的 (n) 个圆,圆心坐标为 (c_i)、半径为 (r_i)。如果若干个圆不相交(相离、相切或包含),则称这些圆符合理论。请你求出符合理论圆的最大集合。

题解

记录路径鲨我>︿<

状态(dp[i][j])表示离散化后(最大(2n))的下标([i,j])区间内符合理论的最大圆数。

转移方程:设(b[i][])为左端点为(i)的圆编号,(cir[i][j])表示是否((1/0))有圆左右端点分别为(i,j)

[dp[i][j]=min(dp[i+1][j],dp[l][b[i][k]]+dp[b[i][k]][r])+cir[i][j]quad (1le i<jle n) ]

没有圆以(i)为左端点则取(dp[i+1][j]),否则枚举以(i)为左端点的圆,以其右端点为断点。如果直接枚举断点时间复杂度为(O(n^3)),且易得只有以圆的端点为断点时可以对答案产生影响。

目标状态(dp[1][cnt])(cnt)为离散化后的下标个数)。

记录路径(pre[i][j])表示(dp[i][j])选取的断点,若从(dp[i+1][j])转移而来则为(-1)。递归,若存在圆以(i,j)为左右端点则输出该圆编号。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=4010;
int c[N],r[N],qwq[N],a[N],b[N],dp[N][N],pre[N][N],cir[N][N],ans[N],tot,cnt;
vector<int> s[N];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') w=-1; ch=getchar();}
	while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
void path(int x,int y)
{
	if(cir[x][y]) ans[++tot]=cir[x][y];
	if(!pre[x][y]) return;
	if(pre[x][y]==-1) path(x+1,y);	
	else path(x,pre[x][y]),path(pre[x][y],y);
}
int main()
{
	int n,x=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d%d",&c[i],&r[i]);
		qwq[++cnt]=c[i]-r[i],qwq[++cnt]=c[i]+r[i];
	}
	sort(qwq+1,qwq+cnt+1); cnt=unique(qwq+1,qwq+cnt+1)-qwq-1;
	for(int i=1;i<=n;i++) 
	{
		a[i]=lower_bound(qwq+1,qwq+cnt+1,c[i]-r[i])-qwq;
		b[i]=lower_bound(qwq+1,qwq+cnt+1,c[i]+r[i])-qwq;
		cir[a[i]][b[i]]=i,s[a[i]].push_back(i);
	}
	for(int i=1;i<=cnt;i++)
	{
		for(int l=1;l+i<=cnt;l++)
		{
			int r=l+i,sz=s[l].size();
			dp[l][r]=dp[l+1][r],pre[l][r]=-1;
			for(int k=0;k<sz;k++) 
			{
				if(b[s[l][k]]>=r) continue;
				if(dp[l][b[s[l][k]]]+dp[b[s[l][k]]][r]>dp[l][r]) 
					dp[l][r]=dp[l][b[s[l][k]]]+dp[b[s[l][k]]][r],pre[l][r]=b[s[l][k]];
			} 
			dp[l][r]+=(cir[l][r]>0);
		}
	} 
	path(1,cnt);
	printf("%d
",tot);
	for(int i=1;i<=tot;i++) printf("%d ",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14818203.html