hihocoder 编程练习赛78 - D 泥泞的道路(最短路)

https://hihocoder.com/contest/offers78/problem/4

 

POINT:

从起点到某个点肯定有多条路,这些路可以抽象为(a,b),a代表经过的路径条数,b代表经过的路径总长度。

那么答案就= a*天数+b。

那么我们就要找到每个点的多对(a,b),当天数较多时,明显a小的有优势,而当天数较少时,可能a大的有优势。 

当然这些比较都是在a确定的情况下,b最小。这个可以用最短路处理出来。

然后每询问一个点,我们去遍历这个点的(a,b)对数,取最小的答案。

 

开dis[x][y],代表走了y条边到x这点的最短距离。

思路就是对于每一个y(每一个a),搜出最小的dis[x][y](最小的b)。

注意到y>=n的时候,就不要搜了,肯定再走重复的路了。

然后我们搜到了所有的dis[x][y],我们对于每一个x,不可能让y=1到y=n,全部都跑一遍。这样肯定是tle的。

维护一个前缀最小值,如果当前的dis[x][y]比这个最小值大,那么这个对就没有价值了。因为之前那个最小值应对的a肯定比这个A小。而b也比这个B=dis[x][y]小。而Ax+B>ax+b一定成立,所以没有价值。(这个对应main函数里的Q次询问之前的预处理操作,主要在qq这个vector里体现)

堆优化重载运算符<是反着的!!,总是忘记,大家当作没看到。

 

#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
#define LL long long
const int N = 1111+55;
const int inf = 0x3f3f3f3f;
vector<int>G[N];
vector<int>W[N];
struct node
{
	int a,b,c;
	node(int aa,int bb,int cc)
	{
		a=aa;b=bb;c=cc;
	}
	bool friend operator < (node a,node b)
	{
		return a.a>b.a;
	}
};
int n,m,Q;

int dis[N][N],vis[N][N];
void dij()
{
	memset(dis,inf,sizeof dis);memset(vis,0,sizeof vis);
	dis[1][0]=0;
	priority_queue<node>q;
	q.push(node(0,1,0));
	while(!q.empty()){
		node x = q.top();
		q.pop();
		int u=x.b,p=x.c;
		if(vis[u][p]||p>n) continue;
		vis[u][p]=1;
		for(int i=0;i<G[u].size();i++){
			int v=G[u][i],w=W[u][i];
			if(dis[v][p+1]>=dis[u][p]+w){
				dis[v][p+1]=dis[u][p]+w;
				q.push(node(dis[v][p+1],v,p+1));
			}
		}
	}

}

vector<node> qq[N];

int main()
{
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=m;i++){
		int x,y,w;
		scanf("%d%d%d",&x,&y,&w);
		G[x].push_back(y);G[y].push_back(x);
		W[x].push_back(w);W[y].push_back(w);
	}
	dij();
	for(int i=2;i<=n;i++){
		int Min=inf;
		for(int j=1;j<=n;j++){
			if(dis[i][j]>=Min) ;
			else{
				Min=dis[i][j];
				qq[i].push_back(node(j,dis[i][j],0));
			}
		}
	}
	while(Q--){
		int i,j;scanf("%d%d",&j,&i);
		if(j==1) printf("0\n");
		else{
			LL ans=0x3f3f3f3f3f3f3f3f;
			for(int k=0;k<qq[j].size();k++){
				//				printf("a=%d b=%d\n",qq[j][k].a,qq[j][k].b);
				ans=min(ans,1LL*i*qq[j][k].a+qq[j][k].b);
			}
			printf("%lld\n",ans);
		}
	}

}




 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值