题解:P1073 [NOIP2009 提高组] 最优贸易
P1073 [NOIP2009 提高组] 最优贸易
原题传送门
更好的阅读体验
解题思路
可以考虑分层图。
什么是分层图,顾名思义,就是有很多层的图,可以用来解决一些能使边权改变的题。
以本题举例。我们可以在任意一个城市进行买入和卖出操作,设现在在
此时,我们可以建三层图,分别代表没买没卖,买了但没卖,买了也卖了。每层内的点互相连边,层与层之间对应点互相连边。层内的点互相连边的边权为
注意事项
- 本题的建图方式比较特殊,第一层的编号就是原编号,第二层的编号是原编号加
n ,第三层的编号是原编号加2\times n ,具体请参见代码。 - 本题存在负权边,需要用 spfa 跑最长路。
- 不要忘记将 dis 数组初始化为最小值。
参考代码
#include<iostream>
#include<queue>
#include<cstring>
#define id(x,y) (x+y*n)//x号点在第y层的编号
using namespace std;
const int N=3e5+5,M=3e6+5;
int n,m,tot;
int a[N],dis[N],head[N];
bool vis[N];
queue<int> q;
struct Edge{
int nxt,to,w;
}edge[M];
void add(int x,int y,int w){
edge[++tot]={head[x],y,w};
head[x]=tot;
}
void spfa(int s){
memset(dis,128,sizeof(dis));//赋初值
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
int t=q.front();
q.pop();
vis[t]=0;
for(int i=head[t];i;i=edge[i].nxt){
int j=edge[i].to;
if(dis[j]<edge[i].w+dis[t]){
dis[j]=edge[i].w+dis[t];
if(!vis[j]){
q.push(j);
vis[j]=1;
}
}
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
add(id(i,0),id(i,1),-a[i]);//第一层向第二层连边
add(id(i,1),id(i,2),a[i]);//第二层向第三层连边
}
for(int i=1,x,y,op;i<=m;i++){
cin>>x>>y>>op;
if(op==1)
for(int j=0;j<=2;j++) add(id(x,j),id(y,j),0);//单向边
else if(op==2)
for(int j=0;j<=2;j++) add(id(x,j),id(y,j),0),add(id(y,j),id(x,j),0);//双向边
}
spfa(1);
cout<<(dis[3*n]<0?0:dis[3*n]);
return 0;
}
AC 记录
最后再推荐几个分层图的题,P2939,P4568,P4822,感兴趣的可以去做一下。