思路:根据题意很容易想到动态规划方程,,状态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; }