NOIP 2009 提高组 第三题 最优贸易

看完这一题,应该可以很容易地想到,要使得差值最大,就要找到1到n的全部路径上,最大值与最小值差值最大的那条路径,并且要保证最小值在最大值的前面,那么可以跑两次spfa,找出1到每个点路径上的最小值,再找到每个点到n的路径上的最大值,最后枚举每一条边(是边不是路径!)的起点和终点,计算出终点到n的路径上的最大值,减去1到起点的路径上的最小值,然后用ans记录这些差值中最大的那个就好了。

代码:

#include <cstdio>
#include <cstring>

int dis[2][1000010];
int n,m,a[1000010],len=0;
struct node{int x,y,next;};
node e[1000010];
int first[1000010];
void buildroad(int x,int y)//邻接表 
{
    len++;
    e[len].x=x;
    e[len].y=y;
    e[len].next=first[x];
    first[x]=len;
}
int road[500010][3];
int q[1000010],st,ed;
bool v[1000010];
int maxx(int x,int y){return x>y?x:y;}
int minn(int x,int y){return x<y?x:y;}
void spfamin()
{
    st=1,ed=2;
    q[st]=1;
    v[1]=true;
    dis[0][1]=a[1];
    while(st!=ed)
    {
        int x=q[st];
        for(int i=first[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(dis[0][y]>minn(a[y],dis[0][x]))
            {
                dis[0][y]=minn(a[y],dis[0][x]);
                if(!v[y])
                {
                    q[ed++]=y;
                    if(ed>n)ed=1;
                    v[y]=true;
                }
            }
        }
        v[x]=false;
        st++;
        if(st>n)st=1;
    }
}
void spfamax()
{
    st=1,ed=2;
    q[st]=n;
    v[1]=true;
    dis[1][n]=a[n];
    while(st!=ed)
    {
        int x=q[st];
        for(int i=first[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(dis[1][y]<maxx(a[y],dis[1][x]))
            {
                dis[1][y]=maxx(a[y],dis[1][x]);
                if(!v[y])
                {
                    q[ed++]=y;
                    if(ed>n)ed=1;
                    v[y]=true;
                }
            }
        }
        v[x]=false;
        st++;
        if(st>n)st=1;
    }
}
void work(int id)//跑两次spfa,一次正向跑,一次反向跑 
{
    memset(first,0,sizeof(first));len=0;
    for(int i=1;i<=m;i++)
    {
        if(road[i][2]-1)buildroad(road[i][1],road[i][0]),buildroad(road[i][0],road[i][1]);//双向边直接建 
        else if(id)buildroad(road[i][1],road[i][0]);
        else buildroad(road[i][0],road[i][1]);
    }
    if(id)spfamax();else spfamin();
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),dis[0][i]=999999999,dis[1][i]=0;
    for(int i=1;i<=m;i++)
    scanf("%d %d %d",&road[i][0],&road[i][1],&road[i][2]);
    memset(v,false,sizeof(v));
    work(1);work(0);
    int ans=0;
    for(int i=1;i<=len;i++)
    {
        int x=e[i].x,y=e[i].y;
        if(dis[0][x]!=999999999&&dis[1][y]!=0&&dis[1][y]-dis[0][x]>ans)ans=dis[1][y]-dis[0][x];//枚举每一条边,用差值更新aans 
    }
    printf("%d",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值