【TJOI 2019】大中锋的游乐场

传送门


problem

游乐场里有 n n n 个娱乐设施,娱乐设施之间有 m m m 条道路相连,经过每一条路都需要花费一定的时间 t t t。每一个娱乐设施旁都会配有一个小卖部,一部分小卖部会销售可乐,另一部分会销售汉堡。

由于大中锋十分贪吃,所以每当他走到一个娱乐设施,他都会先去购买一杯可乐或一个汉堡,并把它们吃掉。但如果大中锋吃掉的汉堡数量比他喝掉的可乐数量多于 k k k,那他就会感到很渴;如果喝掉的可乐数量比吃掉的汉堡数量多于 k k k,那他就会感到很饿。

现在大中锋正在第 a a a 个娱乐设施,他想前往第 b b b 个娱乐设施,但在他前进的路途中他不希望自己很渴或很饿。大中锋想知道自己在路上少花费多少时间。但由于大中锋很懒惰,他不想思考这个问题。你能帮助他解决这个问题吗?

注意:在起始点和终点他也会去买汉堡 / / /可乐,你也需要保证在这两个点他不会感到很饿或者很渴。

数据范围: n ≤ 1 0 4 n≤10^4 n104 m ≤ 1 0 5 m≤10^5 m105 k ≤ 10 k≤10 k10 t ≤ 1 0 4 t≤10^4 t104


solution

我们把可乐的权值赋为 1 1 1,汉堡的权值复赋为 − 1 -1 1,易知一条合法路径上任意时刻的权值 ∈ [ − k , k ] \in[-k,k] [k,k]

那么我们可以设计一个 d p dp dp,设 d [ i ] [ j ] d[i][j] d[i][j] 表示从 a a a 点到 i i i 点权值为 j j j 的最短路径,这可以在 dijkstra 时处理出来。

答案就是 a n s = min ⁡ i = − k k { d [ n ] [ i ] } ans=\min\limits_{i=-k}^k\{d[n][i]\} ans=i=kmink{d[n][i]}

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)


code

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 200005
using namespace std;
int n,m,k,t,p,q;
int val[N],d[N][25],first[N],v[M],w[M],nxt[M];
int Abs(int x)  {return x>0?x:-x;}
void add(int x,int y,int z){
	nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
struct node{
	int x,del,dis;
	node(int x=0,int del=0,int dis=0):x(x),del(del),dis(dis){}
};
bool operator<(const node &p,const node &q)  {return p.dis>q.dis;}
priority_queue<node>Q;
void dijkstra(int S){
	memset(d,0x3f,sizeof(d));
	Q.push(node(S,val[S]+k,0)),d[S][val[S]+k]=0;
	while(!Q.empty()){
		node now=Q.top();Q.pop();
		for(int i=first[now.x];i;i=nxt[i]){
			int to=v[i],num=now.del+val[to];
			if(num<0||num>2*k)  continue;
			if(d[to][num]>now.dis+w[i]){
				d[to][num]=now.dis+w[i];
				Q.push(node(to,num,d[to][num]));
			}
		}
	}
}
int main(){
	int x,y,z,T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d",&n,&m,&k);
		t=0,memset(first,0,sizeof(first));
		for(int i=1;i<=n;++i)  scanf("%d",&x),val[i]=(x==1)?1:-1;
		for(int i=1;i<=m;++i){
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z),add(y,x,z);
		}
		scanf("%d%d",&p,&q),dijkstra(p);
		int ans=0x3f3f3f3f;
		for(int i=0;i<=2*k;++i)  ans=min(ans,d[q][i]);
		printf("%d\n",ans==0x3f3f3f3f?-1:ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值