本题在洛谷上的链接:https://www.luogu.org/problem/show?pid=P1073
不得不说,这是道好(难)题。我确实没有秒掉他,还是看了别人的分析过的。大家的做法有很多,什么Tarjan、并查集之类的,因为最近在学最短路,所以选择了SPFA变形的做法。
就像刘汝佳(而不是紫书上,愤怒地偷笑)对Floyd的变形那样,SPFA也可以变形,从而维护其他信息。
我们可以这样想,对于每个可以到达终点的结点,只要得到从起点到他购买水晶球的最小花费(自然可以从起点到达该结点),那么以这个点为卖出点的收益就确定了,可以以此更新最终答案。
问题是怎么获得这个最小花费呢?我们可以通过SPFA算法(其实Dijkstra也可以)的变形,像求最短路那样求出每个结点的最小花费。但这里需要注意一点,每个结点的最小花费可能是用当前点为买入点得到的,也可能是用之前的某个点为买入点得到的。所以我们应该用啷个数值的最小值来更新这个最小花费。
还有,怎么保证结点可以到达终点呢?可以对应原图建一个反图(每条边都相反),从终点开始进行遍历,可以遍历到的点就是可以到达终点的结点。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 inline int get_num() { //读入优化 8 int num; 9 char c; 10 while((c=getchar())=='\n'||c==' '||c=='\r'); 11 num=c-'0'; 12 while(isdigit(c=getchar())) num=num*10+c-'0'; 13 return num; 14 } 15 const int maxn=1e5+5,maxm=1e6+5,inf=0x3f3f3f3f; 16 int n,m,price[maxn],head1[maxn],head2[maxn],eid,mbuy[maxn],check[maxn],ans; 17 struct edge { 18 int u,v,next; 19 edge(int u=0,int v=0):u(u),v(v) {} 20 } E1[maxm],E2[maxm]; 21 void insert(edge* E,int* head,int u,int v) { 22 E[eid]=edge(u,v); 23 E[eid].next=head[u]; 24 head[u]=eid++; 25 } 26 queue<int> q; 27 void spfa() { 28 memset(mbuy,inf,sizeof(mbuy)); 29 mbuy[1]=price[1]; 30 q.push(1); 31 check[1]=1; 32 while(!q.empty()) { 33 int u=q.front();q.pop(); 34 check[u]=0; 35 for(int p=head1[u];p+1;p=E1[p].next) { 36 int v=E1[p].v; 37 if(mbuy[v]>min(mbuy[u],price[v])) { //注意这里的更新方式,SPFA要保证每次更新都有“成效” 38 mbuy[v]=min(mbuy[u],price[v]); 39 if(!check[v]) {q.push(v);check[v]=1;} 40 } 41 } 42 } 43 } 44 void judge() { 45 q.push(n); 46 check[n]=1; 47 while(!q.empty()) { 48 int u=q.front();q.pop(); 49 for(int p=head2[u];p+1;p=E2[p].next) { 50 int v=E2[p].v; 51 if(!check[v]) { 52 check[v]=1; 53 q.push(v); 54 } 55 } 56 } 57 } 58 int main() { 59 n=get_num();m=get_num(); 60 for(int i=1;i<=n;++i) price[i]=get_num(); 61 memset(head1,-1,sizeof(head1)); 62 memset(head2,-1,sizeof(head2)); 63 int x,y,z; 64 for(int i=1;i<=m;++i) { 65 x=get_num();y=get_num();z=get_num(); 66 if(z==1) {insert(E1,head1,x,y);insert(E2,head2,y,x);} //建原图时,同时建反图 67 else {insert(E1,head1,x,y);insert(E1,head1,y,x);insert(E2,head2,x,y);insert(E2,head2,y,x);} 68 } 69 spfa(); 70 memset(check,0,sizeof(check)); //check数组先用于SPFA中标记结点是否在队列中,BFS用来作为vis数组 71 judge(); 72 for(int i=1;i<=n;++i) 73 if(check[i]) ans=max(ans,price[i]-mbuy[i]); 74 if(ans<=0) printf("0"); //特判“亏本”的情况 75 else printf("%d",ans); 76 return 0; 77 }