2021ccpc 哈尔滨 G 状压dp+期望dp

该博客讨论了一个关于路径规划的问题,其中一个人需要从一个地方出发到达另一个地方,途中有多个地点可以租用单车,单车有坏的可能性。博主提出了使用期望动态规划的解决方案,通过状态转移方程来计算最小的期望总时间。博客内容涵盖了动态规划、概率计算和图论中的最短路径算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

nnn个地方,被mmm条路连接着。某个人走路和汽车的速度分别是t,r(m/s)t,r(m/s)t,r(m/s),其中t≤rt\leq rtr。有k(k≤18)k(k\leq 18)k(k18)个地方有单车,但每个地方的单车有pi100\frac{p_{i}}{100}100pi的概率是坏的。现在这个人要从111nnn去,求出一种方案,使得期望总时间最小,输出这个期望总时间

Solution:

显然我们需要考虑要不要去找单车,并且有关期望,考虑期望dp,再考虑如何遍历单车,看到k≤18k\leq18k18,考虑状压dp,和吃奶酪那题类似,定义dp[i][s]dp[i][s]dp[i][s]为走过集合为sss,目前停留在iii的某种属性如何,由于要考虑期望,不妨设dp[i][s]dp[i][s]dp[i][s]为检查单车集合为sss,目前停留在iii,到nnn的期望时间。

期望dp考虑决策,对每个s,is,is,i,我们有如下两种决策:

(1)当前位置的单车没坏,骑上直接润

(2)当前位置的单车坏了,接下来又有两种可能,再去找单车,或者直接走路润

于是在每个位置,有这样的事件

(单车没坏,骑上出发),(单车坏了,不找了走路去),(单车坏了,再去找单车)

而事件组合则存在两个

(单车没坏,骑上出发)+(单车坏了,不找了走路去)

(单车没坏,骑上出发)+(单车坏了,再去找单车)

那么期望=每个事件的值×概率,接下来推导转移方程

显然全部单车都找过的时候不存在3,是个边界情况,于是

for(int i=1;i<=k;i++) dp[i][(1<<k)-1]=p[i]/100*dis(a[i],n)/t+(100-p[i])/100*dis(a[i],n/r)

并且由于转移方程为:

继续去找单车:

$$
dp[i][s]=\frac{p_{i}}{100}(dp[j][s|1<<(j-1)]+dis(a[i],a[j])/t)+(1-\frac{p_{i}}{100})dis(a[i],n)/r\

$$

不找了直接润:

dp[i][s]=pi100dis(a[i],n)/t+(1−pi100)dis(a[i],n)/r dp[i][s]=\frac{p_{i}}{100}dis(a[i],n)/t+(1-\frac{p_{i}}{100})dis(a[i],n)/r dp[i][s]=100pidis(a[i],n)/t+(1100pi)dis(a[i],n)/r

每次都从比自己大的sss转移而来,于是需要倒推

其中dis(x,y)dis(x,y)dis(x,y)都只有关于1,a[1],a[2],a[3],....,a[k],n1,a[1],a[2],a[3],....,a[k],n1,a[1],a[2],a[3],....,a[k],n这些点,对1,a[1],a[2],....,a[k]1,a[1],a[2],....,a[k]1,a[1],a[2],....,a[k]分别做一共k+1k+1k+1dijkstradijkstradijkstra就可以预处理出来

// #include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

using ll=long long;
const int N=200005,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;

struct way
{
	int to,next,w;
}edge[N<<1];
int cnt,head[N];

void add(int u,int v,int w)
{
	edge[++cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt;
}

int n;
bool vis[N];

void dijkstra(int *dis,int s)
{
	for(int i=1;i<=n;i++) dis[i]=inf,vis[i]=false;
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
	q.push({dis[s]=0,s});
	while(!q.empty())
	{
		int u=q.top().second; q.pop();
		if(vis[u]) continue;
		vis[u]=true;
		for(int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to,w=edge[i].w;
			if(dis[u]+w<dis[v]) q.push({dis[v]=dis[u]+w,v});
		}
	}
}

int m,t,r,k,a[20],p[20],dis[20][N];
double dp[20][1<<18];

int main()
{
	ios::sync_with_stdio(false);
	cin>>t>>r>>n>>m;
	while(m--)
	{
		int u,v,w; cin>>u>>v>>w;
		add(u,v,w); add(v,u,w);
	}
	cin>>k;
	for(int i=1;i<=k;i++)
	{
		cin>>a[i]>>p[i];
		dijkstra(dis[i],a[i]);
	}
	dijkstra(dis[0],n);
	if(dis[0][1]==inf)
	{
		printf("-1");
		return 0;
	}
	for(int i=0;i<=18;i++)
		for(int s=0;s<(1<<18);s++) dp[i][s]=inf;
	for(int i=1;i<=k;i++) dp[i][(1<<k)-1]=1.0*(100-p[i])/100*dis[0][a[i]]/r+1.0*p[i]/100*dis[0][a[i]]/t;
	for(int s=(1<<k)-1;s>=0;s--)
	{
		for(int i=1;i<=k;i++)
		{
			if((s>>(i-1))&1)
			{
				dp[i][s]=1.0*(100-p[i])/100*dis[0][a[i]]/r+1.0*p[i]/100*dis[0][a[i]]/t;
				for(int j=1;j<=k;j++)
				{
					if((s>>(j-1))&1) continue;
					dp[i][s]=min(dp[i][s],1.0*p[i]/100*(dp[j][s|(1<<(j-1))]+1.0*dis[i][a[j]]/t)+1.0*(100-p[i])/100*dis[i][n]/r);
				}
			}
		}
	}
	double ans=1.0*dis[0][1]/t;
	for(int i=1;i<=k;i++) ans=min(ans,1.0*dis[i][1]/t+1.0*dp[i][1<<(i-1)]);
	printf("%.6lf",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值