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\n",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\n",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\n",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\n",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\n",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\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值