装满的油箱
分析
算法
dijkstra+拆点图
时间复杂度
O(MlogNC)
思路
在刚写这题的时候,完全没有任何思路。因为通过分析题目,我们可以知道,其实是类似于求一个最短路。但是又跟典型的最短路不相同。因为需要算的其实是。
从起点走到终点的花费的最小值。但这个花费,让我有点迷茫了,花费需要考虑,在哪加多少油,才能使得车子,到达终点花费才能最少。
因为要素过多,所以迷茫了。但听完y总讲完,有了点思路和扩展。
我们来从头分析一下我们要求的要求,并标注一些重点。
从起点到终点的花费的最小值。花费需要考虑在哪加多少油才能使车子到达终点花费最少。
我们来一点点的分析,最小值,依旧是告诉我们要用最短路来求。
在哪,则是另我们发现,对每一个点来说,它本身拥有的除了编号外,还有另外一个属性到该点的手的汽车油量,到相同点,但油量不同,则依旧是不同的状态点。这里我们发现可以用拆点图的方法将一个点拆分为,编号+油量的状态。
多少油,则是让我们发现了,我们在一个点的加油过程,可以看成是两个点的加边过程
\[(ver,c) => (ver,c+1)
\]
通过+1,我们可以连接出一个编号的所有油量情况。
至此,我们就发现了,建立完拆点图后,题目就变为了求(S,0)=>sE,0)的最短路。
拓展
关于拆点图的一些想法吧,也不能叫拓展。
我们用到拆点图的情况,往往是一个点的情况有很复杂的状态。
但并不一定都很直接的摆在眼前。
例如这道题,我刚开始就被复杂的状态打晕了。但通过分析,我们可以知道,所有的转移,其实只要给点多加上一维状态即可。
但怎么发现,其实并不那么容易。
所以,在遇到一个图论问题时,拆分出所有的状态转移过程,是非常关键的。
拆分后,我们就可以明白一个点的状态有哪些,边的状态有哪些。
ACcode
#include<bits/stdc++.h>
using namespace std;
const int N = 1010,M = 2e4 + 10,C = 110;
int h[N],e[M],ne[M],w[M],idx;
int P[N];
int dist[N][C];
bool st[N][C];
int n,m;
struct Node
{
int d,u,c;
bool operator<(const Node &w) const
{
return d>w.d;
}
};
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int dijkstra(int c,int start,int end)
{
priority_queue<Node> pq;
memset(dist,0x3f,sizeof dist);
memset(st, 0, sizeof st);
dist[start][0]=0;
pq.push({0,start,0});
while(pq.size())
{
auto t = pq.top();
pq.pop();
if(t.u==end) return t.d;
if(st[t.u][t.c]) continue;
st[t.u][t.c]=1;
if(t.c+1<=c)
{
if(dist[t.u][t.c+1]>t.d+P[t.u])
{
dist[t.u][t.c+1]=t.d+P[t.u];
pq.push({dist[t.u][t.c+1],t.u,t.c+1});
}
}
for(int i=h[t.u];~i;i=ne[i])
{
int j = e[i];
if(t.c>=w[i])
{
if(dist[j][t.c-w[i]]>t.d)
{
dist[j][t.c-w[i]]=t.d;
pq.push({dist[j][t.c-w[i]],j,t.c-w[i]});
}
}
}
}
return -1;
}
int main()
{
scanf("%d%d",&n,&m);
memset(h, -1, sizeof h);
for(int i=0;i<n;i++) scanf("%d",&P[i]);
while (m -- )
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
int q;
scanf("%d",&q);
while(q--)
{
int c,s,e;
scanf("%d%d%d",&c,&s,&e);
int t = dijkstra(c,s,e);
if(t==-1) puts("impossible");
else printf("%d\n",t);
}
return 0;
}

浙公网安备 33010602011771号