题意: 有N-1个城市给首都(第N个城市)支援物资,有M条路,走每条路要耗费一定百分比(相对于这条路的起点的物资)的物资。问给定N-1个城市将要提供的物资,和每条路的消耗百分比。求能送到首都的最多的物资数量
思路:求前N-1个点各自到达N是所剩物资的最大值,设dis[i]为到达i城市时所剩的物资,则变为求最长路的问题了,所以要对前N-1个节点都做一次最长路,再累加即可。
要注意的地方:
1)第一是消耗百分比是相对于这条路的起点,第二是无向边。因为第一点,所以不能看成反向的DIJ一次性做完,而是应该把每个点可以送到最多物资依次算出来最后相加。用Bellman松弛边,因为是无向边,而又没有保存两次边,所以每次都要进行正反两次松弛,当然,每次也只能有一个边能松弛到。
2)这道题松弛的变量是d[t], d[s]的大小关系,故能用每次正反松弛的方法。如果松弛关系要为d[t]与d[s]+w的关系,每次正反松弛会互相增长,会无限正圈(无向边)循环下去。。。
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; #define MMV 105 #define MME 100000 int inf = 1000000000; struct edge_t { int s, t; double w; }; edge_t arrEdge[MME]; int szEdge; double dis[MMV]; int nv, ne; int post[MMV]; bool Bellman(int s) { int i, j; for(i = 1; i <= nv; i++) dis[i] = -inf; dis[s] = post[s]; for(i = 1; i < nv; i++) { for(j = 1; j <= szEdge; j++) { //松弛求最长路 edge_t e = arrEdge[j]; if(dis[e.t] < dis[e.s]*(1 - e.w)) dis[e.t] = dis[e.s]*(1 - e.w); if(dis[e.s] < dis[e.t]*(1 - e.w)) dis[e.s] = dis[e.t]*(1 - e.w); } } return true; } int main() { int i, j, s, t; double w; while(scanf("%d %d", &nv, &ne)==2) { memset(post, 0, sizeof(post)); for(i = 1; i < nv; i++) scanf("%d", &post[i]); szEdge = 0; for(i = 1; i <= ne; i++) { scanf("%d %d %lf", &s, &t, &w); ++szEdge; arrEdge[szEdge].s = s; arrEdge[szEdge].t = t; arrEdge[szEdge].w = w; } double sum = 0; for(i = 1; i < nv; i++)//对每一个节点做一次最长路 { if(Bellman(i)) if(dis[nv]>0) sum += dis[nv]; } printf("%.2lf\n", sum); } return 0; }