题意:从1-n的一条路径中,找出两点,使得两点权值之差最大。n个点不一定要都经过。
解题思路:这道题实际可以转化为找出可达的两点a,b,使得a和b两点的权值之差最大。。。这道题确实很难想到是转化为最短路的模型,我开始还是按照论坛里面说的,先求强连通分量、缩点,最后再搜索,结果挂了。。
假设最后的结果是a-b,那么我们肯定要保证a>b,否则根据题意就无意义。并且我们还应该要有b->a,即可以从b走到a,那么就定义两个数组d_min[i],和d_max[i],分别表示从1->i经过点的最小值和从i->n经过点的最大值,最后只需找到最大的d_max[i] - d_min[i]即可。为了方便求d_max[i],我们应该要从n出发寻找,所以应该要建立反向边。
注意:这里是能够保证我们之前讲的两条性质,注意到d_min和d_max表示的意义,是1->i和i->n所经过的点的最小和最大值,如果a在b的前面,那么肯定d_max和d_min不会同时包含a和b的(画图即可明白),如果a<b,毫无疑问肯定算出的不会是最优值。
这里算d_min和d_max可以用spfa算法,确实spfa算法在最短路里面的应用非常广。总之这样运用spfa还是第一次见。。。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int maxn = 100005; struct Edge { int k,next; }edge[maxn<<2]; int n,m,cnt,d_max[maxn],d_min[maxn]; int head1[maxn],head2[maxn]; bool s1[maxn],s2[maxn]; void addedge(int u,int v) { edge[cnt].k = v; edge[cnt].next = head1[u]; head1[u] = cnt++; edge[cnt].k = u; edge[cnt].next = head2[v]; head2[v] = cnt++; } void spfa() { queue<int> q; memset(s1,false,sizeof(s1)); memset(s2,false,sizeof(s2)); q.push(1); s1[1] = true; while(!q.empty()) { int t = q.front(); q.pop(); for(int i = head1[t]; i != -1; i = edge[i].next) { int k = edge[i].k; d_min[k] = min(d_min[t],d_min[k]); if(s1[k] == false) { s1[k] = true; q.push(k); } } } q.push(n); s2[n] = true; while(!q.empty()) { int t = q.front(); q.pop(); for(int i = head2[t]; i != -1; i = edge[i].next) { int k = edge[i].k; d_max[k] = max(d_max[t],d_max[k]); if(s2[k] == false) { s2[k] = true; q.push(k); } } } int ans = 0; for(int i = 1; i <= n; i++) if(s1[i] && s2[i]) { ans = max(ans,d_max[i] - d_min[i]); } printf("%d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); cnt = 0; for(int i = 1; i <= n; i++) { scanf("%d",&d_max[i]); d_min[i] = d_max[i]; } for(int i = 1; i <= m; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); addedge(x,y); if(z == 2) addedge(y,x); } spfa(); } return 0; }