zoukankan      html  css  js  c++  java
  • NOIP2009最优贸易(两遍SPFA/tarjan缩点+拓扑序dp)

    传送门

    本来想做一下tarjan缩点再拓扑序dp的题。

    突然发现原来做过的这道可以用这种方法解决(原来是两遍SPFA)。

    那么就合着一起总结一下吧!

    原来一眼看到这道题,咦这不是水题嘛。

    直接bfs一下找出能够达到1和n的点,然后这些点的max - min就好了呀!

    然后愉快地敲好了代码,交上去全wa。

    #include<bits/stdc++.h>
    #define LL long long 
    #define N 100003
    #define M 500003
    #define INF 2100000000
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    void print(int x)
    {
        if(x<0)x=-x,putchar('-');
        if(x>10)print(x/10);
        putchar(x%10+'0');
    }
    struct EDEG{
        int nextt,to;
    }w[M*2],w2[M*2];
    int head[N],head2[N],val[N];
    int vis[N],vis2[N];
    int tot=0,tot2=0;
    int n,m;
    void add(int a,int b)
    {
        tot++;
        w[tot].nextt=head[a];
        w[tot].to=b;
        head[a]=tot;
    }
    void add_b(int a,int b)
    {
        tot2++;
        w2[tot2].nextt=head2[a];
        w2[tot2].to=b;
        head2[a]=tot2;
    }
    queue<int>q;
    void bfs(int x)
    {
        while(!q.empty())q.pop();
        vis[x]=1;
        q.push(x);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head[x];i;i=w[i].nextt)
            {
                int v=w[i].to;
                if(!vis[v])vis[v]=1,q.push(v);
            }
        }
    }
    void bfs_b(int x)
    {
        while(!q.empty())q.pop();
        vis2[x]=1;
        q.push(x);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head2[x];i;i=w2[i].nextt)
            {
                int v=w2[i].to;
                if(!vis2[v])vis2[v]=1,q.push(v);
            }
        }
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;++i)val[i]=read();
        for(int i=1;i<=m;++i)
        {
            int a=read(),b=read();
            int op=read();
            if(op==1)
            {
                add(a,b);
                add_b(b,a);
            }
            else if(op==2)
            {
                add(a,b);add(b,a);
                add_b(a,b);add_b(b,a);
            }
        } 
        bfs(1);bfs_b(n);
        int maxx=-INF,minn=INF;
        for(int i=1;i<=n;++i)
        {
            if(vis[i]&&vis2[i])
            {
                maxx=max(maxx,val[i]);
                minn=min(minn,val[i]);
            }
        }
        printf("%d
    ",maxx-minn);
    错误代码

    为什么是错的?因为我们要保证先到买入点再到卖出点。

    下面看正解:

    1.两遍SPFA

    容易想到定义两个数组:

    d[i]表示从1到这个点的最小买入值。

    f[i]表示从n到这个点的最大卖出值。

    然后每个点的f[i]-d[i]取个max即可。

    正反SPFA各来一次就可以处理出f和d。

    #include<bits/stdc++.h>
    #define LL long long 
    #define N 100003
    #define M 500003
    #define INF 2100000000
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    void print(int x)
    {
        if(x<0)x=-x,putchar('-');
        if(x>10)print(x/10);
        putchar(x%10+'0');
    }
    struct EDEG{
        int nextt,to;
    }w[M*2],w2[M*2];
    int head[N],head2[N],val[N];
    int vis[N],vis2[N],d[N],f[N];
    int tot=0,tot2=0;
    int n,m;
    void add(int a,int b)
    {
        tot++;
        w[tot].nextt=head[a];
        w[tot].to=b;
        head[a]=tot;
    }
    void add_b(int a,int b)
    {
        tot2++;
        w2[tot2].nextt=head2[a];
        w2[tot2].to=b;
        head2[a]=tot2;
    }
    queue<int>q;
    void SPFA(int u)//最短路 
    {
        while(!q.empty())q.pop();
        for(int i=1;i<=n;++i)
        {
            vis[i]=0;d[i]=INF;
        }
        d[u]=val[u];
        vis[u]=1;
        q.push(u);
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=w[i].nextt)
            {
                int v=w[i].to;
                if(d[v]>min(d[x],val[v]))
                {
                    d[v]=min(d[x],val[v]);
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    void SPFA_b(int u)
    {
        while(!q.empty())q.pop();
        for(int i=1;i<=n;++i)
        {
            vis2[i]=0;f[i]=-INF;
        }
        f[u]=val[u]; 
        vis2[u]=1;
        q.push(u);
        while(!q.empty())
        {
            int x=q.front();q.pop();vis2[x]=0;
            for(int i=head2[x];i;i=w2[i].nextt)
            {
                int v=w2[i].to;
                if(f[v]<max(f[x],val[v]))
                {
                    f[v]=max(f[x],val[v]);
                    if(!vis2[v])
                    {
                        vis2[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;++i)val[i]=read();
        for(int i=1;i<=m;++i)
        {
            int a=read(),b=read();
            int op=read();
            if(op==1)
            {
                add(a,b);
                add_b(b,a);
            }
            else if(op==2)
            {
                add(a,b);add(b,a);
                add_b(a,b);add_b(b,a);
            }
        } 
        SPFA(1);SPFA_b(n);
        int ans=-INF;
        for(int i=1;i<=n;++i)
            ans=max(ans,f[i]-d[i]);
        printf("%d
    ",ans);
    }
    /*
    */
    SPFA

    tarjan缩点+拓扑序dp

    图中可能有环,所以我们会自然地想到缩点。

    缩点之后变成一个DAG,于是我们根据拓扑序dp一下即可。

    注意可能有到不了n的连通块。用数组标记一下即可。

    #include<bits/stdc++.h>
    #define LL long long 
    #define N 100003
    #define M 500003
    #define INF 2100000000
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    void print(int x)
    {
        if(x<0)x=-x,putchar('-');
        if(x>10)print(x/10);
        putchar(x%10+'0');
    }
    struct edge{
        int x,y;
    }e[M*2];
    struct EDEG{
        int nextt,to;
    }w[M*2];
    int head[N],val[N];
    int d[N],f[N];
    int tot=0;
    int n,m;
    void add(int a,int b)
    {
        tot++;
        w[tot].nextt=head[a];
        w[tot].to=b;
        head[a]=tot;
    }
    int vis[N],low[N],dfn[N],stackk[N];
    int maxx[N],minn[N],reach[N],in[N],ans[N],bel[N];
    int timer=0,cnt=0,top=0;
    void tarjan(int x)
    {
        dfn[x]=low[x]=++timer;
        vis[x]=1;stackk[++top]=x;
        for(int i=head[x];i;i=w[i].nextt)
        {
            int v=w[i].to;
            if(!dfn[v])
            {
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else if(vis[v])low[x]=min(low[x],dfn[v]); 
        }
        if(low[x]==dfn[x])
        {
            cnt++;
            maxx[cnt]=-INF;minn[cnt]=INF;
            int t;
            do{
                t=stackk[top--];
                maxx[cnt]=max(maxx[cnt],val[t]);
                minn[cnt]=min(minn[cnt],val[t]);
                vis[t]=0;
                bel[t]=cnt;
            }while(x!=t);
        }
    } 
    queue<int>q;
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;++i)val[i]=read();
        int num=0;
        for(int i=1;i<=m;++i)
        {
            int a=read(),b=read();
            int op=read();
            if(op==1)
            {
                add(a,b);
                e[++num].x=a;e[num].y=b;
            }
            else if(op==2)
            {
                add(a,b);add(b,a);
                e[++num].x=a;e[num].y=b;
                e[++num].x=b;e[num].y=a;
            }
        }
        for(int i=1;i<=n;++i)
          if(!dfn[i])tarjan(i);
        memset(head,0,sizeof(head));
        tot=0;
        for(int i=1;i<=num;++i)
        {
            int a=e[i].x,b=e[i].y;
            if(bel[a]==bel[b])continue;
            add(bel[a],bel[b]);
            in[bel[b]]++;
        }
        reach[bel[1]]=1;
        while(!q.empty())q.pop();
        for(int i=1;i<=cnt;++i)
          if(!in[i])q.push(i);
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for(int i=head[x];i;i=w[i].nextt)
            {
                int v=w[i].to;
                if(reach[x])//要考虑到那些无法到达n的连通块 
                {
                    reach[v]=1;
                    ans[v]=max(ans[v],ans[x]);
                    minn[v]=min(minn[v],minn[x]);
                    ans[v]=max(ans[v],maxx[v]-minn[v]);
                    //ans[i]表示在i这里卖出,所以maxx不与前面取max 
                    //但买入是可以更小的 
                }
                in[v]--;
                if(!in[v])q.push(v);
            }
        }
        printf("%d",ans[bel[n]]);
    }
    /*
    */
    tarjan+拓扑dp
  • 相关阅读:
    SQL SERVER中DBLINK的实现
    如何在 Amazon AWS 上设置一台 Linux 服务器
    如何在 Amazon AWS 上设置一台 Linux 服务器
    django 后台数据直接交给页面
    django locals()
    让MySQL支持Emoji表情 mysql 5.6
    MySQL数据技术嘉年华,带你深入MySQL的世界
    Python爬虫入门教程 17-100 CSDN博客抓取数据
    JAVA生成图片缩略图、JAVA截取图片局部内容
    Python爬虫入门教程 16-100 500px摄影师社区抓取摄影师数据
  • 原文地址:https://www.cnblogs.com/yyys-/p/11852236.html
Copyright © 2011-2022 走看看