题目翻译
给出直线上的 (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;
}