【NOIP2009】最优贸易

本题在洛谷上的链接: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 }
AC代码

猜你喜欢

转载自www.cnblogs.com/Mr94Kevin/p/9544590.html