思路:根据题意很容易想到动态规划方程,,状态dp[i][j]表示到达i时油量剩余j,但是复杂度太高。
从另一个角度想,由一个最优解产生另一个最优解,每次取最优解的节点进行扩展,类似dijkstra最短路。
具体处理如下:
每次取一个最优节点出队,则节点为队中到源点花费最小的,在该状态下,可以选择购买1单位的油,也可以走向其邻节点。为什么是购买1单位的油?假设此时队首油量为j,在该点最多可以购买capacity - j的油,每次增加1个单位,最多再有capacity - j个节点入队,而若每次取出节点时就枚举所有购买的可能性,会造成队列中大量重复节点,故每次只增加一单位。
代码如下:
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define N 1005
#define M 10005
#define C 105
#define INF 0x3f3f3f3f
#define min(a, b) (a) < (b) ? (a) : (b)
int cnt, p[N], head[N], dis[N][C];
bool ins[N][C];
struct Node{
int u, fuel, cost;
Node(int uu, int f, int c): u(uu), fuel(f), cost(c) {}
friend bool operator< (const Node& n1, const Node& n2){
return n1.cost > n2.cost;
}
};
struct Edge{
int to, next, d;
}e[M * 2];
void add(int from, int to, int d){
++cnt;
e[cnt].to = to, e[cnt].d = d, e[cnt].next = head[from], head[from] = cnt;
}
int dijkstra(int source, int dest, int capacity){
memset(dis, INF, sizeof(dis));
memset(ins, false, sizeof(ins));
priority_queue<Node> pq;
pq.push(Node(source, 0, 0));
while(!pq.empty()){
Node cur = pq.top();
pq.pop();
int u = cur.u, fuel = cur.fuel, cost = cur.cost;
if(ins[u][fuel])
continue;
ins[u][fuel] = true;
if(u == dest)
return cost;
if(fuel + 1 <= capacity && !ins[u][fuel + 1] && dis[u][fuel + 1] > cost + p[u]){
pq.push(Node(u, fuel + 1, cost + p[u]));
dis[u][fuel + 1] = cost + p[u];
}
for(int i = head[u]; i != 0; i = e[i].next){
int to = e[i].to, d = e[i].d;
if(fuel >= d && dis[to][fuel - d] > cost && !ins[to][fuel - d] && dis[to][fuel - d] > cost){
pq.push(Node(to, fuel - d, cost));
dis[to][fuel - d] = cost;
}
}
}
return -1;
}
int main(){
int n, m;
scanf("%d %d", &n, &m);
for(int i = 0; i < n; ++i)
scanf("%d", &p[i]);
for(int i = 0; i < m; ++i){
int from, to, d;
scanf("%d %d %d", &from, &to, &d);
add(from, to, d);
add(to, from, d);
}
int q;
scanf("%d", &q);
for(int i = 0; i < q; ++i){
int s, e, c;
scanf("%d %d %d", &c, &s, &e);
int res = dijkstra(s, e, c);
if(res == -1)
printf("impossible\n");
else
printf("%d\n", res);
}
return 0;
}