题意 : n个城市,每个城市的油价不同,m条连通这些城市之间的路,提供油桶的容量,要你求出由城市s出发到城市e的最小费用,其中刚开始时油桶为空,一单位的油可以走以单位的长度。
看到这题目觉得很难,这主要是不会建图和DP的思想.
事实上就是类似dijsktra算法的搜索,搜索最短路。
这个题目的图的点事实上有两个维.
一个是费用,一个是地点,(比如在位置0有1升油是一个点,在位置0有2升油又是另外一个点)
如果把这种点抽象出来,把费用看过边,那么最少费用就可以按照dijsktra算法那样不断的加入点(至于正确性请看dijkstra算法的证明,贪心性质,非负权边)
于是得到一个扩展结点的策略:
1.每一次加一升油(因为题目的条件这些都是整数,所以可以类似离散化处理,一点一点加入油)
2.如果加的油足够走到下一个结点,那就走到下一个结点吧(记得减去路上消耗的油,还有花费不变...)
#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define inf 0x7fffffff
const int MAX=1002;
struct EDGE
{
int v;//终点
int len;//边的长度
int next;
}edge[10009];//一定要边的范围
int head[MAX];
int k;
int n,m;
int S,V,E;
int a[1003];
int dp[1002][102];
bool used[1002][102];
void insert(int u,int v,int len)
{
edge[k].v=v;
edge[k].len=len;
edge[k].next=head[u];
head[u]=k;
k++;//正边
edge[k].v=u;
edge[k].len=len;
edge[k].next=head[v];
head[v]=k;
k++;//反边
}
struct Node
{
int posion;
int vale;
int fule;
friend bool operator <(Node a,Node b)
{
return a.vale>b.vale;
}
};
priority_queue<Node>my;
Node next,now;
int dij(int s)
{
while(!my.empty())
my.pop();
memset(used,0,sizeof(used));
for(int i=1;i<=n;i++)
for(int j=0;j<=100;j++)
dp[i][j]=inf;
now.posion=s;
now.fule=0;
now.vale=0;
my.push(now);
dp[s][0]=0;
used[s][0]=1;
while(!my.empty())
{
now=my.top();
my.pop();
int x=now.posion;
int y=now.fule;
int z=now.vale;
used[x][y]=1;
if(x==E)
return z;
if(y+1<=V&&!used[x][y+1]&&dp[x][y+1]>dp[x][y]+a[x])
{
dp[x][y+1]=dp[x][y]+a[x];
next.fule=y+1;
next.posion=x;
next.vale=dp[x][y+1];
my.push(next);
}
for(int i=head[x];i!=-1;i=edge[i].next)
{
int t=edge[i].v;
int kk=y-edge[i].len;
if(kk>=0&&!used[t][kk]&&z<dp[t][kk])
{
dp[t][kk]=z;
next.posion=t;
next.fule=kk;
next.vale=dp[t][kk];
my.push(next);
}
}
}
return -1;
}
int ans;
int uu,vv,cc;
int cas;
int main()
{
int i;
scanf("%d%d",&n,&m);
{
for( i=1;i<=n;i++)
head[i]=-1;
k=0;
for( i=1;i<=n;i++)
scanf("%d",&a[i]);
for( i=0;i<m;i++)
{
scanf("%d%d%d",&uu,&vv,&cc);
uu++;vv++;
insert(uu,vv,cc);
}
// for(int x=1;x<=n;x++)
// for(int i=head[x];i!=-1;i=edge[i].next)
// {
// cout<<x<<' '<<edge[i].v<<' '<<edge[i].len<<endl;
// }
scanf("%d",&cas);
while(cas--)
{
scanf("%d%d%d",&V,&S,&E);;
S++;E++;
ans=dij(S);
if(ans==-1)
printf("impossible");
else
printf("%d\n",ans);
}
}
return 0;
}