洛谷-换教室-(概率期望+dp)

P1850

题意:
就是给你n节课,每节课刚开始在va[i]教室,然后每节课都可以申请调换,调换到vb[i]教室,申请成功的概率是f[i]。然后再给你每个教室之间的距离,和一共申请的最多次数m。然后必须要按顺序上课,然后问你在上完n节课,走的路程的期望值最小是多少。

思考:
其实第一次做这种,要仔细思考。首先问你路程的期望值,那么如果直接求总的肯定不好求。可以分开很多段去求,对于2到n,每次都要从i-1走到i,对于每个这样的路程都求一次期望,把这些期望加起来就好了。对于有n节课,m次申请,很明显了,定义dp[i][j][0/1]为,上到第i节课,用了j次申请,当前申请或不申请,最小期望总和。那么转移的时候,分好情况就行。由于期望值是所有概率累加起来的,所以转移的时候,如果从dp[i-1][j-1][1]来转移,那么看看要加多少期望,这个期望值是好几种不同的情况累加起来的。然后再转移。这题可以定义恰好用了j次申请,也可以定义不超过j次。只是两者的初始化的时候有些地方不一样。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
using namespace std;
const int mod = 1e9+7,inf = 1e9;
const int N = 2e5+10,M = 2010;

int T,n,m;
int va[M];
int vb[M];
db f[M];
db dist[M][M];
db dp[M][M][2];

signed main()
{
	IOS;
	int v,e;
	cin>>n>>m>>v>>e;
	for(int i=1;i<=n;i++) cin>>va[i];
	for(int i=1;i<=n;i++) cin>>vb[i];
	for(int i=1;i<=n;i++) cin>>f[i];
	for(int i=1;i<=v;i++)
	{
		for(int j=1;j<=v;j++)
		if(i!=j) dist[i][j] = inf;
	}
	while(e--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		dist[b][a] = dist[a][b] = min(dist[a][b],(db)c);
	}
	for(int k=1;k<=v;k++) //先把任意两点最小距离求出来
	{
		for(int i=1;i<=v;i++)
		{
			for(int j=1;j<=v;j++)
			dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);
		}
	}
	for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) dp[i][j][0] = dp[i][j][1] = inf; //定义恰好的用了j次
	dp[1][0][0] = dp[1][1][1] = 0; //初始化边界
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		{
			db sum1 = dp[i-1][j][1],sum2 = dp[i-1][j][0]; //可以从这两种类型转移过来
			sum1 += f[i-1]*dist[vb[i-1]][va[i]]; 
			sum1 += (1-f[i-1])*dist[va[i-1]][va[i]]; //这一种类型就要加上所有情况的概率期望
			sum2 += dist[va[i-1]][va[i]];
			dp[i][j][0] = min(sum1,sum2); //取一个最小的
			if(j<1) continue;
			sum1 = dp[i-1][j-1][1],sum2 = dp[i-1][j-1][0]; //同理也是
			sum1 += f[i-1]*f[i]*dist[vb[i-1]][vb[i]];
			sum1 += (1-f[i-1])*f[i]*dist[va[i-1]][vb[i]];
			sum1 += f[i-1]*(1-f[i])*dist[vb[i-1]][va[i]];
			sum1 += (1-f[i-1])*(1-f[i])*dist[va[i-1]][va[i]];
			sum2 += f[i]*dist[va[i-1]][vb[i]];
			sum2 += (1-f[i])*dist[va[i-1]][va[i]];
			dp[i][j][1] = min(sum1,sum2);
		}
	}
	db anw = inf;
	for(int i=0;i<=m;i++) anw = min(anw,min(dp[n][i][0],dp[n][i][1]));
	printf("%.2lf",anw);
	return 0;
}

总结:
多多思考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值