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 (1\le i<j\le 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\n",tot);
	for(int i=1;i<=tot;i++) printf("%d ",ans[i]);
	return 0;
}
posted @ 2021-05-27 15:49  violet_holmes  阅读(24)  评论(0编辑  收藏  举报