2021 ccpc 哈尔滨 G. Damaged Bicycle

题意:

给了一个图以及不超过18个自行车点,每个自行车点有w概率没有车,给了骑自行车的速度以及步行的速度,问到达终点的最小期望时间

考点:状压dp,最短路

思路:

首先考虑到达终点的期望时间是如何计算的,很显然我要么直接步行去终点,要么走到某一个自行车点然后有概率拿到自行车去终点或者我拿不到自行车,拿不到自行车有两种选择,直接步行去终点或者继续找到另外的自行车重复此次操作,那么一定有一个最终到达的自行车点,我不管拿没拿到自行车都直接去终点。

可以把所有自行车点想象成二进制串,最终结果一定是我按到达顺序选择某一些自行车点到终点的期望,考虑dp,f[s][j]枚举每一种自行车点的选择情况s与最后到达的自行车点 j,表示我按任意顺序经过这些自行车点且最后一个到达的自行车点为 j 的到终点的最小期望,枚举上一个到达的自行车点在哪转移即可,注意每次算距离要用最短距离跑18遍最短路即可

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
const int N=1e5+5;
double f[19][1<<18];
int hard[N];
double lin[19][N];
bool ss[19][N]={0};
ll bj[20],bj1[N];
ll n1;
ll d[100];
double w1[20];
struct duqi{
	int i,end;
	double k;
};
duqi q[N*2];
struct du{
	double jl;
	ll i;
	operator<(du w1)const{
	return jl>w1.jl;
	}
};
du t1,t2;
priority_queue<du>w;
void zdl(ll n){
	ll i,j,l;
	for(i=0;i<=n1;i++)
	for(j=1;j<=n;j++)lin[i][j]=1e12;
	
	for(i=0;i<=n1;i++){
		t1.i=bj[i];t1.jl=0;lin[i][t1.i]=0;
		w.push(t1);
		while(!w.empty()){
			t2=w.top();w.pop();
			
			if(ss[i][t2.i]==1)continue;
			ss[i][t2.i]=1;
			for(j=hard[t2.i];j!=0;j=q[j].end){
				l=q[j].i;
				if(lin[i][l]>q[j].k+lin[i][t2.i]){
					lin[i][l]=q[j].k+lin[i][t2.i];
					t1.i=l;t1.jl=lin[i][l];
					w.push(t1);
				}
			}
		}
	}
}
double gl[1<<18];
int main()
{
	ll i,j,k,l,n,m,sum,t,l1,r,r1,mid,pa,pb,g,max1,p,i1,w2;
	double v1,v2,jl,x,h,min1;
	p=0;
	scanf("%lf%lf",&v1,&v2);
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=m;i++){
		scanf("%lld%lld%lf",&l,&r,&jl);
		q[++p].i=r;q[p].end=hard[l];q[p].k=jl;hard[l]=p;
		q[++p].i=l;q[p].end=hard[r];q[p].k=jl;hard[r]=p;
		
	}
	scanf("%lld",&n1);
	bj[0]=1;
	for(i=1;i<=n1;i++){
		scanf("%lld%lf",&k,&x);
		bj[i]=k;bj1[k]=i;w1[i]=x;
	}
	zdl(n);
	if(lin[0][n]>=1e10){
		printf("-1");return 0;
	}
	d[0]=1;
	for(i=1;i<=20;i++)d[i]=d[i-1]*2;
	for(i=0;i<=d[n1]-1;i++){
		h=1;
		for(j=0;j<n1;j++)if((1<<j)&i)h=h*(w1[j+1])/100;
		gl[i]=h;
	}
	min1=1e12;
	for(j=1;j<=n1;j++){
		f[j][d[j-1]]=lin[0][bj[j]]/v1+lin[j][n]*(1-w1[j]/100)/v2;
		min1=min(min1,f[j][d[j-1]]+gl[d[j-1]]*lin[j][n]/v1);
	}
	g=0;
	for(i=1;i<=d[n1]-1;i++){
		if(i==d[g]){
			g++;continue;
		}
		for(j=1;j<=n1;j++){
			f[j][i]=1e12;
			if((1<<(j-1))&i){
				for(l=1;l<=n1;l++){
					if((1<<l-1)&i&&l!=j){
						f[j][i]=min(f[j][i],f[l][i-d[j-1]]+lin[l][bj[j]]*gl[i-d[j-1]]/v1+gl[i-d[j-1]]*lin[j][n]*(1-w1[j]/100)/v2);
					}
				}
			}
			min1=min(min1,f[j][i]+gl[i]*lin[j][n]/v1);
		}
	}
	printf("%.6lf",min(min1,lin[0][n]/v1));
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值