[CF39C]Moon Craters

题目

传送门

题解

研究了俩小时,终于是看懂了题解

首先简化题目:可以将这些在同一直线上的圆看做是在直线上的一些区间,现在我们要求最多能选多少区间使得这些区间不相交,并输出任意一种最多的选择情况。

然后,我们可以构想一下最后的状态是什么样子:选了一些很大的不相交的区间,在这些区间中又分别选一些不相交的次大的区间,一直迭代直到小的区间中没有更小的区间可选

那么,对于某一区间 \([l,r]\) 我们可以假定有某一 \([l,i]\)\([i,r]\) 的区间(不一定存在)将其分成两个部分,在这俩部分中我们进行递归在 \([l,i]\)\([i,r]\) 中寻找,显然这个 \(i\in [l,r]\),如果我们直接枚举,那么这个算法的复杂度显然为 \(\mathcal O(n^3)\)

考虑对其进行优化,注意到算法瓶颈在于区间 \([l,i]\)\([i,r]\) 都是我们假定存在的,所以我们需要寻找 \([l,r]\) 中的所有 \(i\) 以避免不漏掉小区间,但是我们是否可以直接钦定某一边是存在,另一边是虚构的呢?显然可行,储存以 \(l\) 为左边界的所有区间中, 他们的右边界有哪些,只需要在这些右边界中对区间 \([l,r]\) 进行划分即可,这样优化之后,算法整体的复杂度均摊来算,是 \(\mathcal O(n^2)\)

在区间处理完之后,我们需要考虑是否存在 \([l,r]\) 这样的区间,将整个区间框起来(这是合法的),如果存在加上即可

因为题目还要求我们输出方案,所以我们可以用另一个数组记录一下某一个区间的分界线,在输出方案时亦可递归寻找

对于算法的一个优化,因为区间 \([l,r]\) 的最优性是独立的,所以我们可以用 \(f\) 记录一下某一区间的最优解,再次访问时可直接返回

为了方便处理,其实我们可以将这些区间的左右端点进行离散化,这里不作过多说明

对于方法,我也不知道该说是 \(DP\) 还是记忆化搜索了...

代码

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define erep(i,u) for(signed i=tail[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
typedef long long LL;
// typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef unsigned uint;
#define Endl putchar('\n')
// #define int long long
// #define int unsigned
// #define int unsigned long long

#define cg (c=getchar())
template<class T>inline void read(T& x){
    char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x;
}
template<class T>inline T read(const T sample){
    T x=0;char c;bool f=0;
    while(cg<'0'||'9'<c)f|=(c=='-');
    for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>void fwrit(const T x){//just short,int and long long
    if(x<0)return (void)(putchar('-'),fwrit(-x));
    if(x>9)fwrit(x/10);
    putchar(x%10^48);
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
    inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
    return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}

const int MAXN=2000;
const int MAXSIZE=4000;
const int INF=1e9;

int a[MAXN+5],b[MAXN+5];
int n,x[MAXN*2+5],sz;
int exist[MAXSIZE+5][MAXSIZE+5];//是否存在区间 [i,j] 
vector<int>v[MAXSIZE+5];//以 i 为左边界的所有区间中, 他们的右边界有哪些

inline void Init(){
    n=read(1);
    int c,r;
    rep(i,1,n){
        c=read(1),r=read(1);
        a[i]=c-r,b[i]=c+r;
        x[++sz]=a[i],x[++sz]=b[i];
    }
}

int L,R;
inline void Has_x(){
    sort(x+1,x+sz+1);
    int p1=1,p2=1,p3=1;
    L=INF,R=-INF;
    while((p2=p3)<=sz){
        while(p3<=sz && x[++p3]==x[p2]);
        x[p1++]=x[p2];
    }sz=p1-1;
    rep(i,1,n){
        a[i]=lower_bound(x+1,x+sz+1,a[i])-x;
        b[i]=lower_bound(x+1,x+sz+1,b[i])-x;
        exist[a[i]][b[i]]=i;
        L=Min(L,a[i]),R=Max(R,b[i]);
        v[a[i]].push_back(b[i]);
    }
}

int f[MAXSIZE+5][MAXSIZE+5],s[MAXSIZE+5][MAXSIZE+5];
int Dfs(const int l,const int r){
    // printf("Now l == %d, r == %d\n",l,r);
    if(l>r)return f[l][r]=0;
    if(f[l][r]!=-1)return f[l][r];
    f[l][r]=Dfs(l+1,r);
    for(auto i:v[l]){
        // printf("When l == %d, i == %d\n",l,i);
        if(i>r)continue;
        if(f[l][i]==-1)Dfs(l,i);
        if(f[i][r]==-1)Dfs(i,r);
        if(f[l][i]+f[i][r]>f[l][r]){
            f[l][r]=f[l][i]+f[i][r];
            s[l][r]=i;
        }
    }
    f[l][r]+=(exist[l][r]>0);
    // printf("f[%d,%d] == %d\n",l,r,f[l][r]);
    return f[l][r];
}

void Findpath(const int l,const int r){
    if(l>r)return;
    if(s[l][r])Findpath(l,s[l][r]),Findpath(s[l][r],r);
    else Findpath(l+1,r);
    if(exist[l][r])printf("%d ",exist[l][r]);
}

signed main(){
    Init();
    Has_x();
    memset(f,-1,sizeof f);
    printf("%d\n",Dfs(L,R));
    Findpath(L,R);
    return 0;
}
posted @ 2020-07-28 10:41  Arextre  阅读(214)  评论(0编辑  收藏  举报