[NOIP2009]最优贸易(spfa)

题目描述

传送门

题解

显然题目要求我们选两个点a,b,满足s->a连通,a->b连通,b->t连通,并且b的权与a的权的差值最大。
但是枚举两个点并且判断连通的时间是无法承受的,所以我们要考虑是否枚举一个点就能解决问题。很显然,假设我们枚举的点x在a->b的道路上,那么a一定为s->x的路上权值最小的点,b一定为x->t的路上权值最大的点,来保证有最大的差值。那么问题就又变为对于图中的每一个点,求出起点到这个点能经过的点的最小值和这个点到终点能经过的点的最大值。
完全可以用spfa来解决。分别正向和反向连边,分别从起点和终点做两遍spfa,分别维护最小和最大值就可以了。
还有一点要注意的是,spfa的过程中需要每个点至少加到队列里一次。

然而ATP神犇想出了虽然有点麻烦但是比这个高到不知道哪里去了的方法,必须%%%(mod)*

代码

#include
#include
#include
#include
using namespace std;
#define N 100005
#define M 500005

int n,m,x,y,z,ans;
struct hp{int x,y,z;}edge[M];
int tot,point[N],nxt[M*2],v[M*2];
int pri[N],minn[N],maxn[N];
bool vis[N];
queue <int> q;

void addedge(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void spfa()
{
    for (int i=1;i<=m;++i)
        if (edge[i].z==1) addedge(edge[i].x,edge[i].y);
        else addedge(edge[i].x,edge[i].y),addedge(edge[i].x,edge[i].y);
    memset(minn,127,sizeof(minn)); minn[1]=pri[1];
    memset(vis,0,sizeof(vis)); vis[1]=true;
    while (!q.empty()) q.pop(); q.push(1);

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        vis[now]=false;
        for (int i=point[now];i;i=nxt[i])
        {
            bool flag=false;
            if (minn[v[i]]>pri[v[i]])
            {
                flag=true;
                minn[v[i]]=pri[v[i]];
            }
            if (minn[v[i]]>minn[now])
            {
                flag=true;
                minn[v[i]]=minn[now];
            }
            if (flag&&!vis[v[i]])
            {
                vis[v[i]]=true;
                q.push(v[i]);
            }
        }
    }

    tot=0; memset(point,0,sizeof(point));
    for (int i=1;i<=m;++i)
        if (edge[i].z==1) addedge(edge[i].y,edge[i].x);
        else addedge(edge[i].x,edge[i].y),addedge(edge[i].y,edge[i].x);
    memset(maxn,0,sizeof(maxn)); maxn[n]=pri[n];
    memset(vis,0,sizeof(vis)); vis[n]=true;
    while (!q.empty()) q.pop(); q.push(n);

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        vis[now]=false;
        for (int i=point[now];i;i=nxt[i])
        {
            bool flag=false;
            if (maxn[v[i]]true;
                maxn[v[i]]=pri[v[i]];
            }
            if (maxn[v[i]]true;
                maxn[v[i]]=maxn[now];
            }
            if (flag&&!vis[v[i]])
            {
                vis[v[i]]=true;
                q.push(v[i]);
            }
        }       
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) scanf("%d",&pri[i]);
    for (int i=1;i<=m;++i) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    spfa();
    for (int i=1;i<=n;++i) ans=max(ans,maxn[i]-minn[i]);
    printf("%d\n",ans);
}

总结

①考虑问题不够仔细,分析不够多!图论的题以后要记住有可能有正反两遍spfa。
②如果不是每一个点至少加入一次的话,很多的路就要被切断了。要注意这一点。

你可能感兴趣的:(题解,NOIP,图论算法)