题目链接
题意:
给定起点(xs,ys),
终点(xd,yd),
最大旅程距离B
汽车的每公里代价C0
其余交通方式数量T<=100
其余交通方式代价[C1…CT]<=100
车站数量N<=1000
第i个车站的位置(xi,yi),边数li<=100,每条边(j,mj)表示连向第j个车站,交通方式为mj
可以开车从s到任一车站或目的地,从任一车站开车到目的地,但是车站间不得开车
问在旅程B内的最小代价
简化:无向图,起点s,终点t,边(u,v,a,b),要求s->t的路径边权b<=B条件下,边权a最小
直接用二维dij算法,d[u][j]表示到点u,累计路程b==j情况下代价w的最小值
思路:首先这题给出的数据比较复杂,用链式前向星建图时间效率更优。
每条边存公里数b,代价w。
于是问题简化成,在一个有双重边权的图里面,求出出发点到终点的最短路程(代价w最小),且另一个的权重积累不能超过B
//二维迪杰斯特拉算法问题,把dist数组以及vis数组改成2维 优先队列的节点改成3维即可
注意数据大小maxn要2e5 ,因为边数可能达到2e5
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define INF 0x3f3f3f
#define LL long long
const int maxn=2e5+5;
struct
{
int x,y;
}p[maxn];//点坐标
struct Node
{
int u,tw,tb;//顶点信息 ,i到u的最优co2tw,对应的公里数tb
friend bool operator < (const Node &a,const Node &b)
{
if(a.tw!=b.tw) return a.tw>b.tw;
else return a.tb>b.tb;
}
};//优先队列节点 w小的优先
struct node
{
int to;
int w,b;//w是co2排放量 b是公里数
int next;
}edge[maxn];//链式前向星建图,优化时间
int head[maxn];//以i为顶点的插入的最后一条边所在edge的下标
int cnt=1;//边的总数,从1开始
vector<pair<int,int> >G[maxn];//第i个点的所有映射
int sx,sy,dx,dy,B,c0,T,n;
int c[maxn];
int dist[1005][105];//二维迪杰斯特拉算法,走到第i个顶点 公里数为j时的最优解
bool vis[1005][105];//vis也要对应变成二维的
void add(int v,int to,int w,int b)//链式前向星插入边
{
edge[cnt].b=b;
edge[cnt].to=to;
edge[cnt].w=w;
edge[cnt].next=head[v];//edge[i].next 表示和第cnt条边同起点的上一条边所在下标,而最终i是最后插入的边
head[v]=cnt++;//head[i]表示以i为起点的,最后一条插入的边的下标
}
int calc(int x1,int y1,int x2,int y2)
{
return ceil(sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));//向上取整算两点距离
}
//用链式前向星建图,每个边存co2排放量 公里数
//问题转化成了:在一个有双重边权的图里面,求出出发点到终点的最短路程(co2),且另一个权重不能超过B
//经典的二维迪杰斯特拉算法问题,把dist数组改成2维 优先队列的节点改成3维即可
void build()
{
for(int i=1;i<=n;i++)//车站之间建双向边
{
for(auto j:G[i])
{
int dis=calc(p[i].x,p[i].y,p[j.first].x,p[j.first].y);
add(i,j.first,dis*c[j.second],dis);
add(j.first,i,dis*c[j.second],dis);
}
}
//起点终点分别设为n+1 n+2号顶点,也得建边
int dis=calc(sx,sy,dx,dy);
add(n+1,n+2,dis*c0,dis);//起点到终点的单向边
//接下来起点和车站 车站和终点建边
for(int i=1;i<=n;i++)
{
int dis1=calc(sx,sy,p[i].x,p[i].y);
add(n+1,i,dis1*c0,dis1);
dis1=calc(dx,dy,p[i].x,p[i].y);
add(i,n+2,dis1*c0,dis1);
}
}
void dijkstra()
{
priority_queue<Node> q;
fill(dist[0],dist[0]+1005*105,INF);
q.push({n+1,0,0});//起点入队
while(!q.empty())
{
Node temp=q.top();
q.pop();
if(vis[temp.u][temp.tb]) continue;
vis[temp.u][temp.tb]=1;
for(int i=head[temp.u];i;i=edge[i].next)
{
int u=edge[i].to;
//这个状态没被标记 && 不超过B && 代价w更小
if(!vis[u][temp.tb+edge[i].b] && temp.tb+edge[i].b<=B &&
edge[i].w+temp.tw<dist[u][temp.tb+edge[i].b])
{
dist[u][temp.tb+edge[i].b]=edge[i].w+temp.tw;
q.push({u,dist[u][temp.tb+edge[i].b],temp.tb+edge[i].b});
}
}
}
}
int main()
{
scanf("%d%d%d%d%d%d%d",&sx,&sy,&dx,&dy,&B,&c0,&T);
for(int i=1;i<=T;i++) scanf("%d",c+i);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
int m;
scanf("%d",&m);
while(m--)
{
int j,cj;
scanf("%d%d",&j,&cj);
G[i].push_back({j+1,cj});//每个顶点映射这样一个集合,用于建图
}
}
build();
dijkstra();
int ans=INF;
for(int i=0;i<=B;i++) ans=min(ans,dist[n+2][i]);
if(ans==INF) ans=-1;
printf("%d\n",ans);
system("pause");
return 0;
}