洛谷P1073 最优贸易——双bfs,或双spfa,或Tarjan缩点+topsort

题目:https://www.luogu.org/problem/P1073

本题极好。

现将多种解法列举如下。

方法一:希望知道在到某点的最小价格,及某点及之后的最大价格。因此想到建原图G与反图G'。结点权值为该点买入或卖出价。边权为0——因此采用bfs,在原图中求出截止到某点处的最小价格mincost[i],在反图中求出在某点及之后的最大价格maxcost[i]。ans=max{maxcost[i]-mincost[i]},i=1,2,...,n。

方法二:边(u,v)权重为点v处的价格。这样就引入三角形不等式进行松驰,从而可以转化为在原图中求最短路,在反图中求最长路。因为结点数N=10万,边数E=50万,故宜采用spfa,其平均时间复杂图O(2E),不建议采用Dijkstra算法(O(N^2))及Ford算法(O(NE))

方法三:因为可能存在环,故先Tarjan缩点,这样就避免了环的干扰,然后再用拓扑序。

重点提示:缩点后不要用bfs,否则第2个点会被卡。大家可以画出bfs树,就知道错的原因了。画出topsotr树就会更明白

下面给出一个样例,以说明缩点后用bfs是不行的:

 

5 5
1 1 50 1 1
1 2 1
2 3 1
3 4 1
1 4 1
4 5 1
答案是49。

文章最后给出一个dfs解题的错解,虽是错解,但也过了7个点,大家画出dfs搜索树,就能明白为什么会被卡点。

用dfs可以解题,但要一个很巧妙的剪枝,以后有时间补上。

AC代码一(方法一):

//双bfs 
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MaxN=1e5+5;
const int MaxE=1e6+5;
int n,m,x,y,z;
int a[MaxN],num,fnum,head[MaxN],fhead[MaxN],
	maxcost[MaxN],mincost[MaxN],team[3*MaxN];
bool exist[MaxN];
struct E{
    int from,to,next;
}e[MaxE],fe[MaxE];
void add(int from,int to){
    e[++num].next=head[from];
    e[num].from=from;
    e[num].to=to;
    head[from]=num;
}
void fadd(int from,int to){
    fe[++fnum].next=fhead[from];
    fe[fnum].from=from;
    fe[fnum].to=to;
    fhead[from]=fnum;
}
void bfs()
{
    memset(exist,0,sizeof(exist));
    int u,v,hd=0,tail=1;
    team[1]=1;
	exist[1]=1;//入栈染色
	mincost[1]=a[1];
    do{
        u=team[++hd];
        for(int j=head[u];j>0;j=e[j].next){
            v=e[j].to;
            mincost[v]=min(mincost[u],a[v]);
            if(!exist[v]){//如果没有入栈
                exist[v]=1;//入栈染色
                team[++tail]=v;
            }
        }   
    }while(hd<tail);
}
void fbfs()
{
    memset(exist,0,sizeof(exist));
    int u,v,hd=0,tail=1;
    team[1]=n;
	exist[n]=1;//入栈染色 
	maxcost[n]=a[n];
    do{
        u=team[++hd];
        for(int j=fhead[u];j>0;j=fe[j].next){
            v=fe[j].to;
            maxcost[v]=max(maxcost[u],a[v]);
            if(!exist[v]){//如果没有入栈 
                exist[v]=1;//入栈染色
                team[++tail]=v;
            }
        }   
    }while(hd<tail);
}
int main(){
	//freopen("trade9.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y);fadd(y,x);
        if(z==2){
            add(y,x);fadd(x,y);
        }   
    }
    bfs();
    fbfs();
    int ans=maxcost[1]-mincost[1];
    for(int i=2;i<=n;++i)
		if(ans<maxcost[i]-mincost[i])
			ans=maxcost[i]-mincost[i];
    printf("%d\n",ans);
    return 0;
}

AC代码二(方法二):

//双spfa 
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MaxN=1e5+5;
const int MaxE=1e6+5;
int n,m,x,y,z;
int a[MaxN],num,fnum,head[MaxN],fhead[MaxN],
	maxcost[MaxN],mincost[MaxN],team[3*MaxN];
bool exist[MaxN];
struct E{
    int from,to,cost,next;
}e[MaxE],fe[MaxE];
void add(int from,int to,int cost){
    e[++num].next=head[from];
    e[num].from=from;
    e[num].to=to;
    e[num].cost=cost;
    head[from]=num;
}
void fadd(int from,int to,int cost){
    fe[++fnum].next=fhead[from];
    fe[fnum].from=from;
    fe[fnum].to=to;
    fe[fnum].cost=cost;
    fhead[from]=fnum;
}
void spfa(){
    memset(exist,0,sizeof(exist));
    memset(mincost,0x3f,sizeof(mincost));
    int u,v,hd=0,tail=1;
    team[1]=1;
	exist[1]=1;//入栈
	mincost[1]=a[1];//不能写成mincost[0]=0 
    while(hd<tail){
        u=team[++hd];
        exist[u]=0;//出栈 
        for(int j=head[u];j>0;j=e[j].next){
            v=e[j].to;
            if(mincost[v]>min(mincost[u],e[j].cost)){
            	mincost[v]=min(mincost[u],e[j].cost); 
				if(!exist[v]){//如果不在栈内 
	                team[++tail]=v;
	                exist[v]=1;//入栈
	            }	
            }      
        }   
    }
}
void fspfa(){
    memset(exist,0,sizeof(exist));
    memset(maxcost,0,sizeof(maxcost));
    int u,v,hd=0,tail=1;
    team[1]=n;
	exist[n]=1;//入栈染色 
	maxcost[n]=a[n];//不能写成maxcost[n+1]=0 
    while(hd<tail){
        u=team[++hd];
        exist[u]=0;//出栈 
        for(int j=fhead[u];j>0;j=fe[j].next){
            v=fe[j].to;
            if(maxcost[v]<max(maxcost[u],fe[j].cost)){
            	maxcost[v]=max(maxcost[u],fe[j].cost);
				if(!exist[v]){//如果不在栈内 
	                team[++tail]=v;
	                exist[v]=1;//入栈
	            }	
            }      
        }   
    }
}
int main(){
	freopen("trade9.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,a[y]);fadd(y,x,a[x]);
        if(z==2){
            add(y,x,a[x]);fadd(x,y,a[y]);
        }   
    }
    spfa();
    fspfa();
    int ans=maxcost[1]-mincost[1];
    for(int i=2;i<=n;++i)
		if(ans<maxcost[i]-mincost[i])
			ans=maxcost[i]-mincost[i];
    printf("%d\n",ans);
    return 0;
}

AC代码三(方法三):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
struct Node{
    int from,to,nex;
};
queue<int> q;
Node edge[1000010];
bool flag[100010];
int ans[100010],rd[100010],cd[100010],minn[100010],maxx[100010],n,m,cost[100010],mi[100010],ma[100010],low[100010],dfn[100010],head[100010],stack[100010],stack_top,group[100010],group_num,tim,num;
int comp(const Node &a,const Node &b){
    if(group[a.from]!=group[b.from]) return group[a.from]>group[b.from];
    return group[a.to]>group[b.to];
}
void tarjan(int u){
    tim++;
    low[u]=dfn[u]=tim;
    stack_top++;
    stack[stack_top]=u;
    int now=head[u];
    while(now!=-1){
        int v=edge[now].to;
        if(dfn[v]==0){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(group[v]==0) low[u]=min(low[u],dfn[v]);
        now=edge[now].nex;
    }
    if(dfn[u]==low[u]){
        group_num++;
        group[u]=group_num;
        minn[group_num]=maxx[group_num]=cost[u];
        while(stack[stack_top]!=u){
            group[stack[stack_top]]=group_num;
            minn[group_num]=min(minn[group_num],cost[stack[stack_top]]);
            maxx[group_num]=max(maxx[group_num],cost[stack[stack_top]]);
            stack_top--;
        }
        stack_top--;
    }
    return;
}
void add(int from,int to){
    num++;
    edge[num].from=from;
    edge[num].to=to;
    edge[num].nex=head[from];
    head[from]=num;
}

int main(){
    int x,y,z,u,val,v;
    ios::sync_with_stdio(false);
    memset(head,-1,sizeof(head));
    memset(flag,false,sizeof(flag));
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>cost[i];
    }
    for(int i=1;i<=m;i++){
        cin>>x>>y>>z;
        add(x,y);
        if(z==2) add(y,x);
    }
    for(int i=1;i<=n;i++){
        if(dfn[i]==0) tarjan(i);
    }
    sort(edge+1,edge+num+1,comp);
    memset(head,-1,sizeof(head));
    z=num;
    num=0;
    for(int i=1;i<=z;i++){
        u=edge[i].from;v=edge[i].to;
        if(group[u]!=group[v]&&(group[u]!=group[edge[i-1].from]||group[v]!=group[edge[i-1].to])){
            add(group[u],group[v]);
            rd[group[v]]++;
            //cout<<group[u]<<" "<<group[v]<<endl;
        }
    }
    
    /******************topsort*******************/
    for(int i=1;i<=group_num;i++)
		if(rd[i]==0){
    		q.push(i);//入栈
    		ans[i]=maxx[i]-minn[i];
    		//flag[i]=true;
    	};

    while(!q.empty()){
        u=q.front();
        int now=head[u];
        q.pop();
        while(now!=-1){
            v=edge[now].to;
            minn[v]=min(minn[v],minn[u]);
            ans[v]=max(ans[u],max(ans[v],maxx[v]-minn[v]));
            rd[v]--;
            if(rd[v]==0){
	                //printf("%d-->%d\n",u,v);
	                q.push(v);//入栈 
	            }
            now=edge[now].nex;
        } 
    }
    /******************topsort*******************/

    cout<<ans[group[n]];
    return 0;
}

错解(可以通过7个测试点):

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int MaxN=1e5+5,MaxE=1e6+5;
int num,nxt[MaxE],head[MaxN],to[MaxE],from[MaxE],vis[MaxE],mincost[MaxN],ans[MaxN];;
int n,m,a[MaxN],x,y,z;
void join(int x,int y){
	nxt[++num]=head[x];
	head[x]=num;
	to[num]=y;
	from[num]=x;
}
void dfs(int u){
	for(int i=head[u];i;i=nxt[i]){
		vis[i]++;
		if(vis[i]>2)return;
		//边i不能走第三遍。不能写成vis[i]==2,
		//否则还会尝试第3,4,……,次
		int v=to[i];
		mincost[v]=min(mincost[u],a[v]);
		
		ans[v]=max(ans[u],a[v]-mincost[v]);
		cout<<u<<' '<<ans[u]<<' '<<v<<' '<<ans[v]<<endl; 
		//如果采用ans=max(ans,a[v]-mincost[v],
		//则无法处理从1出发但不是以n为终点的叉路 
		dfs(v);	
	}
}
int main(){
	freopen("trade0.in","r",stdin);
	//freopen("out.out","w",stdout);
	cin>>n>>m;	
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		mincost[i]=1e5;
		ans[i]=0;
	}
	mincost[1]=a[1];
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		join(x,y);
		if(z==2)join(y,x);
	}
	dfs(1);
	printf("%d\n",ans[n]);
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值