题目: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;
}