NOIP最优贸易
#include<cstdio> #define MXN 100000+1 #define MXM 10*MXN int getint(){ int x=0,w=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') w=0; c=getchar(); } while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+(c-'0'); c=getchar(); } return w?x:-x; } void write(int x){ if(x<0){ putchar('-'); x=-x; } if(x>9) write(x/10); putchar(x%10+'0'); return; } int n,m,x,y,z,p,end; int price[MXN],dis[3*MXN+2]; bool vis[3*MXN+2]; int queue[MXM],head,tail; int u[3*MXM+2],v[3*MXM+2],w[3*MXM+2],fst[3*MXN+2],nxt[3*MXM+2],top; void add(int x,int y,int z){ top++; u[top]=x,v[top]=y,w[top]=z; nxt[top]=fst[u[top]]; fst[u[top]]=top; return; } void spfa(){ for(int i=2;i<=3*n+1;i++) dis[i]=2000000000; dis[1]=0; vis[1]=true; queue[tail++]=1; int now; while(head!=tail){ now=queue[head]; vis[now]=false; for(int i=fst[now];i;i=nxt[i]){ if(dis[v[i]]>dis[now]+w[i]){ dis[v[i]]=dis[now]+w[i]; if(vis[v[i]]==false){ queue[tail]=v[i]; vis[v[i]]=true; tail=tail+1==MXM ? 0 : tail+1; } } } head=head+1==MXM ? 0 : head+1; } return; } int main(){ n=getint(),m=getint(); for(int i=1;i<=n;i++) price[i]=getint(); for(int i=0;i<m;i++){ x=getint(),y=getint(),p=getint(); if(p==1){ add(x,y,0),add(x+n,y+n,0),add(x+2*n,y+2*n,0); add(x,y+n,price[x]),add(x+n,y+2*n,-price[x]); } if(p==2){ add(x,y,0),add(y,x,0); add(x+n,y+n,0),add(y+n,x+n,0); add(x+2*n,y+2*n,0),add(y+2*n,x+2*n,0); add(x,y+n,price[x]),add(y,x+n,price[y]); add(x+n,y+2*n,-price[x]),add(y+n,x+2*n,-price[y]); } } end=3*n+1; add(n,end,0),add(3*n,end,0); spfa(); write(-dis[end]); return 0; }
这是一道图论题。
题意是:有n个城市之间有m条道路,每个城市都可以买到一种商品叫水晶球,同时也可以卖出水晶球,买卖价格相同但各个城市之间价格不同,记为price。商人阿龙于是准备在旅游期间倒卖水晶球。他想好好享受旅游,所以只准备做一次交易(即买一次卖一次)。他的旅行路线是从城市1到城市n,但既不必每个城市都去,也不限制每个城市只去一次。有时候城市之间的道路是单向的,但有时是双向的,但无论怎么样都算作一条道路。
输入n,m,下一行输入各城市水晶球的价格,下m行每行输入x,y,z,表示x,y之间有一条道路,z=1时指单向,z=2时指双向。
输出阿龙一次交易可以赚得的最大差价。
虽然第一眼看这道题,感觉有点像本人之前做过的一道题,那道题题解是把spfa的dis数组改成二维的了。但是发现不能这么做,因为完全不一样ORZ……
看了别人题解发现这道题可以用分层图来做:第一层表示买之前,第二层表示卖之前,第三层表示卖之后。若x→y,则在第一层x连接一条到第二层y的边权为price[x]的边,并在第二层x连接一条到第三层y的边权为-price[x]的边。同层之间连接0权边。最后设置汇点T,使第一层的点n和第三层的点n连接到T,从第一层点1开始spfa。
由于题里说阿龙也可以不做买卖,所以第一层的n也连接T。
由于这样跑最短路跑出来的dis[T]≤0,所以最后输出相反数。也可以把一、二层之间的边改为负数,二、三层之间改为正数跑最长路。
这道题很有趣,用分层图做代码短效率高,非常舒服。