zoukankan      html  css  js  c++  java
  • hdu1839(二分+优先队列,bfs+优先队列与spfa的区别)

    题意:有n个点,标号为点1到点n,每条路有两个属性,一个是经过经过这条路要的时间,一个是这条可以承受的容量。现在给出n个点,m条边,时间t;需要求在时间t的范围内,从点1到点n可以承受的最大容量........

    思路:其实我是觉得思路挺简单的,就是二分枚举每条边的容量,然后再看在这个容量的限制下,是否可以从点1到点n........

    方法1:二分枚举边的容量,然后一次dfs,判断在容量和时间的双重限制下,是否可以从点1到达点n......

    wa代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef __int64 ss;
    struct node
    {
        ss k;
        ss t;
        ss v;
    };
    ss n,m,t,a[50005],sum,flag;
    vector<node>vet[10005];
    ss vist[10005];
    void dfs(ss x,ss maxn,ss total)
    {
        if(x==n)
        {
            flag=1;
            return;
        }
        if(flag==1)
        return;
        for(ss i=0;i<vet[x].size();i++)
        {
            node p=vet[x][i];
            if(!vist[p.k]&&p.v>=maxn&&(p.t+total<=t))
            {
                vist[p.k]=1;
                dfs(p.k,maxn,p.t+total);
            }
        }
    }
    ss deal(ss num)
    {
        ss maxn=a[num];
        flag=0;
        memset(vist,0,sizeof(vist));
        dfs(1,maxn,0);
        return flag;
    }
    int main()
    {
        int text;
        scanf("%d",&text);
        while(text--)
        {
            ss cnt=0;
            scanf("%I64d%I64d%I64d",&n,&m,&t);
            for(int i=0;i<=n;i++)
            vet[i].clear();
            for(ss i=0;i<m;i++)
            {
                ss v1,v2,tmp,tmp1;
                scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1);
                node p;
                p.k=v2;
                p.t=tmp1;
                p.v=tmp;
                vet[v1].push_back(p);
                p.k=v1;
                vet[v2].push_back(p);
                a[cnt++]=tmp;
            }
            sort(a,a+cnt);
            //printf("%I64d
    ",cnt);
            ss ll=0,rr=cnt-1;
            ss ans=0;
            while(ll<=rr)
            {
                sum=0;
                ss mid=(ll+rr)/2;
                if(deal(mid))
                {
                    if(ans<a[mid])
                    ans=a[mid];
                    ll=mid+1;
                }
                else  rr=mid-1;
            }
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    

     我倒是很快明白了过来,错误在什么地方。因为我的dfs是每个点只历遍一次,有的路被忽略掉了,从而导致wa,额,可以改改,让dfs回溯,然后重置vist标记数组,但是这样做的话,肯定超时...<..>

    方法2:二分枚举边的容量,然后用优先队列+bfs,判断在容量和时间的双重限制下,是否可以从点1到达点n......

    wa代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef __int64 ss;
    struct node
    {
        ss k;
        ss t;
        ss v;
    };
    struct node1
    {
        friend bool operator<(const node1 a,const node1 b)
        {
            if(a.t>b.t)
                return 1;
            else
                return 0;
        }
        ss e;
        ss t;
    };
    
    ss n,m,t,a[500005],sum,flag;
    vector<node>vet[100005];
    ss vist[100005];
    /*void dfs(ss x,ss maxn,ss total)
    {
        if(x==n)
        {
            flag=1;
            return;
        }
        if(flag==1)
        return;
        for(ss i=0;i<vet[x].size();i++)
        {
            node p=vet[x][i];
            if(!vist[p.k]&&p.v>=maxn&&(p.t+total<=t))
            {
                vist[p.k]=1;
                dfs(p.k,maxn,p.t+total);
                vist[p.k]=0;
            }
        }
    }*/
    priority_queue<node1>q;
    ss bfs(ss num)
    {
        ss minx=a[num];
        node1 p;
        memset(vist,0,sizeof(vist));
        p.e=1;
        p.t=0;
        while(!q.empty())
        q.pop();
        q.push(p);
        vist[p.e]=1;
        while(!q.empty())
        {
            p=q.top();
            q.pop();
            vist[p.e]=0;
            if(p.e==n)
            {
                if(p.t<=t)
                {
                    return 1;
                }
                return 0;
            }
            ss x=p.e;
            for(ss i=0; i<vet[x].size(); i++)
            {
                node p1=vet[x][i];
                node1 iter;
                iter.e=p1.k;
                iter.t=p1.t+p.t;
                if(!vist[p1.k]&&p1.v>=minx&&iter.t<=t)
                {
                    vist[p1.k]=1;
                    q.push(iter);
                }
            }
        }
        return 0;
    }
    int main()
    {
        int text;
        scanf("%d",&text);
        while(text--)
        {
            ss cnt=0;
            scanf("%I64d%I64d%I64d",&n,&m,&t);
            for(ss i=0; i<=n; i++)
                vet[i].clear();
            for(ss i=0; i<m; i++)
            {
                ss v1,v2,tmp,tmp1;
                scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1);
                node p;
                p.k=v2;
                p.t=tmp1;
                p.v=tmp;
                vet[v1].push_back(p);
                p.k=v1;
                vet[v2].push_back(p);
                a[cnt++]=tmp;
            }
            sort(a,a+cnt);
            //printf("%I64d
    ",cnt);
            ss ll=0,rr=cnt-1;
            ss ans=0;
            while(ll<=rr)
            {
                //sum=0;
                ss mid=(ll+rr)/2;
                if(bfs(mid))
                {
                    if(ans<a[mid])
                        ans=a[mid];
                    ll=mid+1;
                }
                else  rr=mid-1;
            }
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    

     思考了很久,也是明白了为什么会wa。在以往,我使用bfs、bfs+优先队列求最小值、最短路什么的时候,都是在一张点图上求解的。换句话说,那样的图上,一个点到另一个点可以到达,那么它们必然相邻,然后路径值固定是1,当然也许会有从某个点到另一个点,路径不是1的,假设从这个点到那个点的距离是k,那么从其他相邻点到那个点的距离也是k,如此就可以使得bfs搜索过去的每个状态只需更新一次,因为更新完一次必然是最小的。

    但是,这个题目却是不同。因为它并不是点有权值,而是边的权值,假如我从点1到点3这个点权值为9,那么从点2到点3的权值可以为3......如此就会导致搜索过去,更新了的状态并不是最小的........

    这就是bfs与spfa的区别吧,bfs可以实现的时候,必然是点自身带权值,比如说,点1有两个属性,一个是xx,一个是yy,要是从其他可以到达点1的点,那么必须加上这个点的某个权值,然后求最小值........

    而spfa却是边带权值.........

    方法3:二分+spfa

    ac代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef __int64 ss;
    struct node
    {
        ss k;
        ss t;
        ss v;
    };
    ss n,m,t,a[50005],sum,flag;
    vector<node>vet[10005];
    ss vist[10005],dis[10005];
    ss spfa(ss num)
    {
        ss minx=a[num];
        for(ss i=0; i<=n; i++)
        {
            dis[i]=((ss)1<<25);
            vist[i]=0;
        }
        dis[1]=0;
        vist[1]=1;
        queue<ss>q;
        q.push(1);
        while(!q.empty())
        {
            ss x=q.front();
            q.pop();
            vist[x]=0;
            for(ss i=0; i<vet[x].size(); i++)
            {
                node p=vet[x][i];
                if(p.v>=minx)
                {
                    if(dis[p.k]>dis[x]+p.t)
                    {
                        dis[p.k]=dis[x]+p.t;
                        if(!vist[p.k])
                            q.push(p.k);
                        vist[p.k]=1;
                    }
    
                }
            }
        }
        if(dis[n]<=t)
            return 1;
        else  return 0;
    }
    int main()
    {
        int text;
        scanf("%d",&text);
        while(text--)
        {
            ss cnt=0;
            scanf("%I64d%I64d%I64d",&n,&m,&t);
            for(ss i=0; i<=n; i++)
                vet[i].clear();
            for(ss i=0; i<m; i++)
            {
                ss v1,v2,tmp,tmp1;
                scanf("%I64d %I64d %I64d %I64d",&v1,&v2,&tmp,&tmp1);
                node p;
                p.k=v2;
                p.t=tmp1;
                p.v=tmp;
                vet[v1].push_back(p);
                p.k=v1;
                vet[v2].push_back(p);
                a[cnt++]=tmp;
            }
            sort(a,a+cnt);
            //printf("%I64d
    ",cnt);
            ss ll=0,rr=cnt-1;
            ss ans=0;
            while(ll<=rr)
            {
                //sum=0;
                ss mid=(ll+rr)/2;
                if(spfa(mid))
                {
                    if(ans<a[mid])
                        ans=a[mid];
                    ll=mid+1;
                }
                else  rr=mid-1;
            }
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    轮播 margin-left实现
    点击按钮切换图片
    运用把不同的方式排版,涉及到float box-flox box-orient
    chrome中font-size<12px时并不更改字体大小仍未12px
    js实现跑马灯
    支付宝支付集成
    前端技术博客
    在iphone5/5s出现界面显示不全,大小为iphone4/4s 的问题
    UIImage使用总结
    在IOS开发中使用自定义的字体
  • 原文地址:https://www.cnblogs.com/ziyi--caolu/p/3461689.html
Copyright © 2011-2022 走看看