CF39C MOON CRATERS

MOON CRATERS

题目传送门

http://codeforces.com/problemset/problem/39/C

参考题解!:
CF39C MOON CRATERS_第1张图片
CF39C MOON CRATERS_第2张图片

除去这道题本身的思考不谈,考察的是比较基础的区间DP
emmm众所周知,CF的题以思维含量大为显著特征,一些算法光会并没有什么用,要分析具体题目并针对之作出相应应对。

比赛的时候看到这道题时,题目看错了,我以为的是选中的圆必须要满足相切,相离或者相包含其中一种的最优值,然后没想到怎么处理后效性,就打了一个很脑残的DP
没想到还有18分

代码如下:
就用当前的情况去推后面的情况,这样子做的话相离应该是对的,另外没法判断后效性所以就emmmm

#include
#include
#include
using namespace std;

struct node
{
    long long r,x,s1,s2; 
} a[2010];
bool cmp(node c,node b)
{
    return c.xx;
}
bool cmp2(node c,node b)
{
    return c.s2int 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]q[i]+1)
              {
                fq[j]=fq[i]+1;
                pathq[j]=i;
              } //相切 
          }
    for (int i=1; i<=n; i++)
      if (anslfor (int i=1; i<=n; i++)
      if (ansq=a[j].s1&&a[i].s2<=a[j].s2&&fb[j]1)
              {
                fb[j]=fb[i]+1;
                pathb[j]=i;
              }
        }//包含 
    for (int i=1; i<=n; i++)
      if (ansbif (ansb>=ansl&&ansb>=ansq)
      {
        cout<return 0;
      }
    if (ansl>=ansb&&ansl>=ansq)
      {
        cout<return 0;
      }
    if (ansq>=ansl&&ansq>=ansb)
      {
        cout<q<

然后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 
#include 
#include 
using namespace std;
#define rep(i,n) for (int i=0;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
      if (y[b[l][k]]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);
}

你可能感兴趣的:(模拟赛训练)