zoukankan      html  css  js  c++  java
  • 洛谷 1084 疫情控制——二分答案+贪心(贪心思路!)

    题目:https://www.luogu.org/problemnew/show/P1084

    二分答案、倍增往上走都很好想,关键是怎么贪心……

    先写了一个贪心,让能走到根的军队中可以待在原孩子的先待在那,然后看看根的哪些孩子未满足,从剩下的中双指针地走。

    忘了给根的孩子的边权排序,40分。改了以后70分……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=5e4+5,Lm=16;
    int n,m,pos[N],hd[N],xnt,nxt[N<<1],to[N<<1],w[N<<1];
    int sta[N],top,tmp[N],tot,jl[N],jnt,pre[N][Lm+5],bh[N];
    ll dis[N],l,r,mid,ans=-1;
    bool vis[N],use[N];
    int rdn()
    {
        int ret=0;bool fx=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
        while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return fx?ret:-ret;
    }
    bool cmp(int u,int v)
    {return dis[u]>dis[v];}//先走距离远的
    void add(int x,int y,int z)
    {
        to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt; w[xnt]=z;
        to[++xnt]=x; nxt[xnt]=hd[y]; hd[y]=xnt; w[xnt]=z;
        r+=z;
    }
    void dfs(int cr,int fa,int b)
    {
        pre[cr][0]=fa; bh[cr]=b;//bh:记录在根的哪个孩子
        for(int i=1,d;i<=Lm;i++)
        {
            d=pre[cr][i-1]; if(!pre[d][i-1])break;
            pre[cr][i]=pre[d][i-1];
        }
        for(int i=hd[cr],v;i;i=nxt[i])
            if((v=to[i])!=fa)
            {
                dis[v]=dis[cr]+w[i]; dfs(v,cr,fa?b:v);
                //r=max(r,dis[v]);//不是!!!
            }
    }
    void dfsx(int cr,int fa)
    {
        if(vis[cr]||!nxt[hd[cr]])return;
        for(int i=hd[cr],v=to[i];i;i=nxt[i],v=to[i])
            if(v!=fa)
            {
                dfsx(v,cr); if(!vis[v]) return;
            }
        vis[cr]=1;
    }
    bool solve()
    {
        memset(vis,0,sizeof vis);
        top=0;
        for(int i=1,nw=pos[i],b=bh[nw];i<=m;i++,nw=pos[i],b=bh[nw])
        {
            ll lm=dis[nw]-mid;
            for(int j=Lm;j>=0;j--)
                if(dis[pre[nw][j]]>=lm&&pre[nw][j])
                    nw=pre[nw][j];
            vis[nw]=1;
            //printf("i=%d nw=%d
    ",i,nw);
            if(nw==1) sta[++top]=pos[i];//按剩余从小到大排序过了
        }
    
        for(int i=hd[1];i;i=nxt[i])
            dfsx(to[i],1);
    
        for(int i=1;i<=jnt;i++) use[jl[i]]=0;
        jnt=0;
        for(int i=1;i<=top;i++)
        {
            //printf("bh[%d]=%d vis=%d
    ",sta[i],bh[sta[i]],vis[bh[sta[i]]]);
            if(!vis[bh[sta[i]]])
            {
                vis[bh[sta[i]]]=1,use[i]=1;
                jl[++jnt]=i;
            }
        }
    
        tot=0;
        for(int i=hd[1];i;i=nxt[i])
        {
            //printf("to[i]=%d vis[%d]=%d
    ",to[i],to[i],vis[to[i]]);
            if(!vis[to[i]])tmp[++tot]=w[i];
        }
    
        sort(tmp+1,tmp+tot+1);//!
        int p0=1;
        //if(mid==1000)printf("tot=%d top=%d jnt=%d
    ",tot,top,jnt);
        for(int i=1;i<=tot;i++)
        {
            //if(mid==1000)printf("tmp[%d]=%d
    ",i,tmp[i]);
            while(p0<top&&(use[p0]||mid-dis[sta[p0]]<tmp[i]))
                p0++;
            //if(mid==1000)printf("sta[%d]=%d res=%lld
    ",p0,sta[p0],mid-dis[sta[p0]]);
            if(p0>top||use[p0]||mid-dis[sta[p0]]<tmp[i])
            {
                //if(mid==1000)
                //    printf("tmp=%d sta[%d]=%d res=%lld(top=%d)
    ",tmp[i],p0,sta[p0],mid-dis[sta[p0]],top);
                return 0;
            }
            p0++;
        }
        return 1;
    }
    int main()
    {
        //freopen("data.in","r",stdin);
        //freopen("testdata.in","r",stdin);
        //freopen("zj.out","w",stdout);
        n=rdn();
        for(int i=1,u,v,z;i<n;i++)
        {
            u=rdn();v=rdn();z=rdn();
            add(u,v,z);
        }
        dfs(1,0,0);
        m=rdn();
        for(int i=1;i<=m;i++) pos[i]=rdn();
        sort(pos+1,pos+m+1,cmp);
        while(l<=r)
        {
            mid=l+r>>1ll;
            if(solve()) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    然后想到了反例。就是可以让走到根、剩余较少的军队覆盖一个离根近的孩子,让那个孩子的军队走上来,去覆盖别的孩子。如果离根近的孩子的军队剩余得很多的话,就能覆盖掉别的军队覆盖不了的孩子了。

    于是换贪心策略。先双指针地分配,如果一个军队走不了了,再看看它是否需要回到自己原来的孩子,不能的话就不管它了。

    然而还是70分……

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=5e4+5,Lm=16;
    int n,m,pos[N],hd[N],xnt,to[N<<1],nxt[N<<1],w[N<<1];
    int dis[N],pre[N][Lm+5],bh[N],fe[N];
    int sta[N],top,tmp[N],tot;
    ll ans,mid,l,r;
    bool vis[N];
    int rdn()
    {
        int ret=0;bool fx=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
        while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return fx?ret:-ret;
    }
    void add(int x,int y,int z)
    {
        to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt; w[xnt]=z;
        to[++xnt]=x; nxt[xnt]=hd[y]; hd[y]=xnt; w[xnt]=z;
    }
    void dfs(int cr,int f,int b)
    {
        pre[cr][0]=f;
        for(int i=1,d;i<=Lm;i++)
        {
            d=pre[cr][i-1];
            if(!d)break;
            pre[cr][i]=pre[d][i-1];
        }
    
        bh[cr]=b;
        for(int i=hd[cr],v;i;i=nxt[i])
            if((v=to[i])!=f)
            {
                dis[v]=dis[cr]+w[i];
                fe[v]=w[i];
                dfs(v,cr,f?b:v);
            }
    }
    bool cmp(int u,int v){return dis[u]>dis[v];}
    void dfsx(int cr,int f)
    {
        if((cr>1&&vis[cr])||!nxt[hd[cr]])return;
        for(int i=hd[cr],v;i;i=nxt[i])
            if((v=to[i])!=f)
            {
                dfsx(v,cr);
                if(!vis[v]&&cr>1)return;
            }
        vis[cr]=1;
    }
    bool cmp2(int u,int v){return fe[u]<fe[v];}
    bool solve()
    {
        memset(vis,0,sizeof vis); top=0;
        for(int i=1,nw=pos[i];i<=m;i++,nw=pos[i])
        {
            ll lm=dis[nw]-mid;
            for(int j=Lm;j>=0;j--)
                if(pre[nw][j]&&dis[pre[nw][j]]>=lm)
                    nw=pre[nw][j];
            vis[nw]=1;
            if(nw==1)sta[++top]=pos[i];
        }
        dfsx(1,0);
        tot=0;
        for(int i=hd[1];i;i=nxt[i])
            if(!vis[to[i]]) tmp[++tot]=to[i];
        sort(tmp+1,tmp+tot+1,cmp2);
    
        int p0=top;
        for(int i=tot;i;i--)
        {
            if(vis[tmp[i]])continue;
            while(p0>1&&mid-dis[sta[p0]]<fe[tmp[i]])
            {
                if(!vis[bh[sta[p0]]]) vis[bh[sta[p0]]]=1;
                p0--;
            }
            if(p0<=0||mid-dis[sta[p0]]<fe[tmp[i]])
                return 0;
            p0--;
        }
        return 1;
    }
    int main()
    {
        //freopen("testdata.in","r",stdin);
        n=rdn();
        for(int i=1,u,v,z;i<n;i++)
        {
            u=rdn(); v=rdn(); z=rdn();
            add(u,v,z); r+=z<<1;
        }
        dfs(1,0,0);
        m=rdn();
        for(int i=1;i<=m;i++) pos[i]=rdn();
        sort(pos+1,pos+m+1,cmp);
        while(l<=r)
        {
            mid=l+r>>1;
            if(solve())ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    终于去查了题解。原来应该以军队为主,从小到大遍历军队,如果它的原来的孩子还没被管,就让它管它原来的孩子;否则让它管一个根的最小的孩子,即正常的双指针。

    这样贪心的话,一个军队要么是双指针地管了最小的需要管的孩子,要么是更优地管了自己原来的孩子;既不像第一个贪心那样,可能浪费路程长的军队,又不像第二个贪心那样,可能浪费了军队可以随便回到自己原来孩子的性质。真是……

    自己还要多锻炼思维呢。

    代码里注意给双指针走过的孩子也打上 vis 标记。因为进来一个军队时要判断那个。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=5e4+5,Lm=16;
    int n,m,pos[N],hd[N],xnt,to[N<<1],nxt[N<<1],w[N<<1];
    int dis[N],pre[N][Lm+5],bh[N],fe[N];
    int sta[N],top,tmp[N],tot;
    ll ans=-1,mid,l,r;
    bool vis[N];
    int rdn()
    {
        int ret=0;bool fx=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
        while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return fx?ret:-ret;
    }
    void add(int x,int y,int z)
    {
        to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt; w[xnt]=z;
        to[++xnt]=x; nxt[xnt]=hd[y]; hd[y]=xnt; w[xnt]=z;
    }
    void dfs(int cr,int f,int b)
    {
        pre[cr][0]=f;
        for(int i=1,d;i<=Lm;i++)
        {
            d=pre[cr][i-1];
            if(!d)break;
            pre[cr][i]=pre[d][i-1];
        }
    
        bh[cr]=b;
        for(int i=hd[cr],v;i;i=nxt[i])
            if((v=to[i])!=f)
            {
                dis[v]=dis[cr]+w[i];
                fe[v]=w[i];
                dfs(v,cr,f?b:v);
            }
    }
    bool cmp(int u,int v){return dis[u]>dis[v];}
    void dfsx(int cr,int f)
    {
        if((cr>1&&vis[cr])||(!nxt[hd[cr]]&&cr>1))return;//cr>1
        for(int i=hd[cr],v;i;i=nxt[i])
            if((v=to[i])!=f)
            {
                dfsx(v,cr);
                if(!vis[v]&&cr>1)return;
            }
        vis[cr]=1;
    }
    bool cmp2(int u,int v){return fe[u]<fe[v];}
    bool solve()
    {
        memset(vis,0,sizeof vis); top=0;
        for(int i=1,nw=pos[i];i<=m;i++,nw=pos[i])
        {
            ll lm=dis[nw]-mid;
            for(int j=Lm;j>=0;j--)
                if(pre[nw][j]&&dis[pre[nw][j]]>=lm)
                    nw=pre[nw][j];
            vis[nw]=1;
            if(nw==1)sta[++top]=pos[i];
        }
    
        //for(int i=1;i<=top;i++) printf("sta[%d]=%d
    ",i,sta[i]);
        
        dfsx(1,0);
        tot=0;
        for(int i=hd[1];i;i=nxt[i])
            if(!vis[to[i]]) tmp[++tot]=to[i];
        sort(tmp+1,tmp+tot+1,cmp2);
    
        //for(int i=1;i<=tot;i++) printf("tmp[%d]=%d
    ",i,tmp[i]);
    
        int p0=1;
        for(int i=1;i<=top;i++)
        {
            if(!vis[bh[sta[i]]])
            {
                vis[bh[sta[i]]]=1;
                //if(mid==11)printf("vis[bh[%d]=%d]=1",sta[i],bh[sta[i]]);
                continue;
            }
            while(p0<tot&&vis[tmp[p0]])p0++;
            if(vis[tmp[p0]])return 1;
            //if(mid==11) printf("tmp[%d]=%d fe=%d mid-dis[%d]=%lld
    ",p0,tmp[p0],fe[tmp[p0]],sta[i],mid-dis[sta[i]]);
            if(mid-dis[sta[i]]>=fe[tmp[p0]])
            {
                vis[tmp[p0]]=1; p0++;//vis!!
                if(p0>tot)return 1;
            }
        }
        while(p0<tot&&vis[tmp[p0]])p0++;
        return vis[tmp[p0]];
    }
    int main()
    {
        //freopen("testdata.in","r",stdin);
        n=rdn();
        for(int i=1,u,v,z;i<n;i++)
        {
            u=rdn(); v=rdn(); z=rdn();
            add(u,v,z); r+=z<<1;
        }
        dfs(1,0,0);
        m=rdn();
        for(int i=1;i<=m;i++) pos[i]=rdn();
        sort(pos+1,pos+m+1,cmp);
        while(l<=r)
        {
            //printf("l=%lld r=%lld
    ",l,r);
            mid=l+r>>1ll;
            if(solve())ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    DirectShow音频采集pcm,实时编码AAC,附源码
    live555 RTSP推送到Darwin出现404错误的解决
    live555 RTSP推送到Darwin出现404错误的解决
    公网RTSP地址(持续更新)
    公网RTSP地址(持续更新)
    手机Android音视频采集与直播推送,实现单兵、移动监控类应用
    手机Android音视频采集与直播推送,实现单兵、移动监控类应用
    用Darwin开发RTSP级联服务器(拉模式转发)(附源码)
    用Darwin开发RTSP级联服务器(拉模式转发)(附源码)
    TCP/IP和Socket的关系
  • 原文地址:https://www.cnblogs.com/Narh/p/9690109.html
Copyright © 2011-2022 走看看