洛谷-题解 P2564【[SCOI2009]生日礼物】

老师集训的题,猛然发现自己的解法居然独一无二!

解法就是:\(dfs!\)再加上2层2分。是不是有点惊人?而且并没有数据卡掉了我这种接近暴力的解法。

分析可得,\(k\)小的惊人,所以\(dfs\)也就可以了。而朴素的\(dfs\)绝对过不了,所以要加上优化。

外层的二分就是来优化的,其实更多的还是让思路更清晰,这个二分是二分答案。这样,\(dfs\)就成了判断了,可以进行剪枝了。

然而,这个优化仍微不足道,在\(dfs\)里有一个贪心的优化。

假设现在的范围为\([x,y]\),搜到了第\(i\)个品种,那么如果有位置在\([x,y]\)间的,那么直接返回\(dfs(i+1,x,y)\)的值,因为这样必定为最优。如果没有,那么就返回\(dfs(i+1,t,y)|dfs(i+1,x,l)\),(\(t\)为小于\(x\)的最大值,\(l\)为大于\(y\)的最小值)这样,再加上前面的剪枝,就可以卡过时限了。

然而,还是担心会被卡,那就是里层二分的作用了。因为枚举上面的\(t\)可能会超时,因为位置都是有序的,可以用二分查找,这样时间效率提高了一些。

剩下的就是一下调试细节了。
下面是集训时交的代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
#include <queue>
using namespace std;
#define re register int
inline int read(){
    int x=0,ff=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*ff;
} 
int n,k,a[65][1000005],tt[65],opt,mx=0,mn=2147483647;
bool dfs(int i,int x,int y){
    if(i>k)return (y-x)<=opt;
    if(y-x>opt)return 0;
    int j=1,t,ll=1,rr=tt[i],mid;
    //while(a[i][j]<x&&j<=tt[i])j++;
    while(ll<=rr){
        mid=(ll+rr)>>1;
        if(a[i][mid]>=x)rr=mid-1;
        else ll=mid+1;
    }
    j=ll;
    if(a[i][j]==x)return dfs(i+1,x,y);
    if(j>tt[i])return dfs(i+1,a[i][tt[i]],y);
    if(a[i][j]>=x&&a[i][j]<=y)return dfs(i+1,x,y);
    if(j>1){
        t=j-1;
        if(y-a[i][t]<=opt)
        if(dfs(i+1,a[i][t],y))return 1;
    }
    if(a[i][j]-x<=opt)return dfs(i+1,x,a[i][j]);
    return 0;
}
bool ok(int x){
    opt=x;
    for(int i=1;i<=tt[1];i++){
        if(dfs(2,a[1][i],a[1][i]))return 1;
    }
    return 0;
}
signed main(){
    n=read();k=read();int l,r,mid;
    for(int i=1;i<=k;i++){
        tt[i]=read();
        for(int j=1;j<=tt[i];j++){
            a[i][j]=read();
        }
        mx=max(mx,a[i][tt[i]]);mn=min(mn,a[i][1]);
    }
    if(k==1){cout<<0<<endl;return 0;}
    l=0;r=mx-mn;
    while(l<=r){
        mid=(l+r)>>1;
        if(ok(mid))r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

考试后,听同学说洛谷有原题,于是交了上去,\(AC\)了。一阵高兴加激动。毕竟解法挺新奇。
然而......老师那儿爆零了!\(rank\)掉了10位。

原来数组开爆了,洛谷的评测是看运行空间。。。。

于是数组改成\(vector\)就过了。。。。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
#include <queue>
using namespace std;
#define re register int
inline int read(){
    int x=0,ff=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x*ff;
} 
int n,k,tt[65],opt,mx=0,mn=2147483647;
vector<int>a[65];
bool dfs(int i,int x,int y){
    if(i>k)return (y-x)<=opt;
    if(y-x>opt)return 0;
    int j=1,t,ll=0,rr=tt[i]-1,mid;
    //while(a[i][j]<x&&j<=tt[i])j++;
    while(ll<=rr){
        mid=(ll+rr)>>1;
        if(a[i][mid]>=x)rr=mid-1;
        else ll=mid+1;
    }
    j=ll;
    if(a[i][j]==x)return dfs(i+1,x,y);
    if(j>=tt[i])return dfs(i+1,a[i][tt[i]],y);
    if(a[i][j]>=x&&a[i][j]<=y)return dfs(i+1,x,y);
    if(j>0){
        t=j-1;
        if(y-a[i][t]<=opt)
        if(dfs(i+1,a[i][t],y))return 1;
    }
    if(a[i][j]-x<=opt)return dfs(i+1,x,a[i][j]);
    return 0;
}
bool ok(int x){
    opt=x;
    for(int i=0;i<tt[1];i++){
        if(dfs(2,a[1][i],a[1][i]))return 1;
    }
    return 0;
}
signed main(){
    n=read();k=read();int l,r,mid;
    for(int i=1;i<=k;i++){
        tt[i]=read();
        for(int j=1;j<=tt[i];j++){
            //a[i][j]=read();
            a[i].push_back(read());
        }
        mx=max(mx,a[i][tt[i]-1]);mn=min(mn,a[i][0]);
    }
    if(k==1){cout<<0<<endl;return 0;}
    l=0;r=mx-mn;
    while(l<=r){
        mid=(l+r)>>1;
        if(ok(mid))r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

所以,算紧了时间,不要忘了空间啊!!!

posted @ 2019-08-10 22:52  sjcx  阅读(154)  评论(0编辑  收藏  举报