题意: 有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;
}