MOON CRATERS
题目传送门
参考题解!:
除去这道题本身的思考不谈,考察的是比较基础的区间DP
emmm众所周知,CF的题以思维含量大为显著特征,一些算法光会并没有什么用,要分析具体题目并针对之作出相应应对。
比赛的时候看到这道题时,题目看错了,我以为的是选中的圆必须要满足相切,相离或者相包含其中一种的最优值,然后没想到怎么处理后效性,就打了一个很脑残的DP
没想到还有18分
代码如下:
就用当前的情况去推后面的情况,这样子做的话相离应该是对的,另外没法判断后效性所以就emmmm
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
long long r,x,s1,s2;
} a[2010];
bool cmp(node c,node b)
{
return c.x<b.x;
}
bool cmp2(node c,node b)
{
return c.s2<b.s2;
}
int n,m,fl[2010],pathl[2010],ansl,ali;
int fq[2010],pathq[2010],ansq,aqi;
int fb[2010],pathb[2010],ansb,abi;
void printb(int x)
{
if (pathb[x]==0)
{
cout<<x<<' ';
return;
}
printb(pathb[x]);
cout<<x<<' ';
}
void printl(int x)
{
if (pathl[x]==0)
{
cout<<x<<' ';
return;
}
printb(pathl[x]);
cout<<x<<' ';
}
void printq(int x)
{
if (pathq[x]==0)
{
cout<<x;
return;
}
printb(pathq[x]);
cout<<x<<' ';
}
int main()
{
freopen("crater.in","r",stdin);
freopen("crater.out","w",stdout);
scanf("%d",&n);
for (int i=1; i<=n; i++)
{
scanf("%d%d",&a[i].x,&a[i].r);
a[i].s1=a[i].x-a[i].r;
a[i].s2=a[i].x+a[i].r;
fq[i]=fl[i]=fb[i]=1;
}
sort(a+1,a+n+1,cmp);
for (int i=1; i<=n; i++)
for (int j=i+1; j<=n; j++)
{
if (a[j].s1>a[i].s2&&fl[i]+1>fl[j])
{
fl[j]=fl[i]+1;
pathl[j]=i;
}//求相离
if ((a[j].s1==a[i].s1||a[i].s2==a[j].s1||a[i].s2==a[j].s2)&&fq[j]<fq[i]+1)
{
fq[j]=fq[i]+1;
pathq[j]=i;
} //相切
}
for (int i=1; i<=n; i++)
if (ansl<fl[i]) ansl=fl[i],ali=i;
for (int i=1; i<=n; i++)
if (ansq<fq[i]) ansq=fq[i],aqi=i;
sort(a+1,a+n+1,cmp2);
for (int i=1; i<=n; i++)
for (int j=i+1; j<=n; j++)
{
if (a[i].s1>=a[j].s1&&a[i].s2<=a[j].s2&&fb[j]<fb[i]+1)
{
fb[j]=fb[i]+1;
pathb[j]=i;
}
}//包含
for (int i=1; i<=n; i++)
if (ansb<fb[i]) ansb=fb[i],abi=i;
if (ansb>=ansl&&ansb>=ansq)
{
cout<<ansb<<endl;
printb(abi);
return 0;
}
if (ansl>=ansb&&ansl>=ansq)
{
cout<<ansl<<endl;
printl(ali);
return 0;
}
if (ansq>=ansl&&ansq>=ansb)
{
cout<<ansq<<endl;
printq(aqi);
return 0;
}
}
然后ak做法
考虑到后效性的话,
具体做法:
我们把所有圆的端点都记下来,排序去重(去重的原因,比如说,1 1 1 1 2,不去重的话,1离散化成了 1 2 3 4 ,2就变成了 5,这个结合后面),后离散化(比如说, 10 15 20 25 30,离散化成1 2 3 4 5,这样可以避免DP时那些不需要的坐标的重复计算),然后,再以每个圆的起始端点和结束端点(l,r)作为dp的区间,做区间dp,
f[r]=max(f[r-1[,f[l]+w[t]),w数组就是这个区间内能够相离或者外切的最大圆数,
w[i]=f[maxr]+1.
注意点是端点必须要分割在每一个圆的起始和结尾。
最后再给出神仙代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i,n) for (int i=0;i<n;i++)
const int N=4010;
int n,c,r,x[N],y[N],d[N],g[N][N],f[N][N],s[N][N],t;
vector<int> b[N];
int dp(int l,int r) {
if (l>r) return 0;
if (f[l][r]!=-1) return f[l][r];
f[l][r]=dp(l+1,r);
for (int k=0; k<b[l].size(); k++)
if (y[b[l][k]]<r&&dp(l,y[b[l][k]])+dp(y[b[l][k]],r)>f[l][r])
f[l][r]=f[l][y[b[l][k]]]+f[y[b[l][k]]][r],s[l][r]=y[b[l][k]];
f[l][r]+=!!g[l][r];
return f[l][r];
}
void dfs(int l,int r) {
if (l>r) return;
if (s[l][r]) dfs(l,s[l][r]),dfs(s[l][r],r);else dfs(l+1,r);
if (g[l][r]) printf("%d ",g[l][r]);
}
int main() {
freopen("crater.in","r",stdin);
freopen("crater.out","w",stdout);
scanf("%d",&n);
rep(i,n) scanf("%d%d",&c,&r),x[i]=c-r,y[i]=c+r,d[i]=x[i],d[i+n]=y[i];
sort(d,d+n+n);
t=unique(d,d+n+n)-d;//排序去重
rep(i,n)
{
x[i]=upper_bound(d,d+t,x[i])-d;y[i]=upper_bound(d,d+t,y[i])-d;
b[x[i]].push_back(i);g[x[i]][y[i]]=i+1;//离散化
}
memset(f,-1,sizeof(f));
printf("%d\n",dp(1,t));
dfs(1,t);
}