【 NOIP 2016 换教室 】【 期望DP 】

 

研读数据,我们发现,28分是可以轻松拿到的:

对于的一个点,直接puts("0.00");即可。

其余的标紫色的数据点,跑一遍最短路,依次相加各个教室之间的最短路径长度即可。

其实,↑ 也是建立在你知道期望是啥的基础上的。

对于本体题解,请见落谷题解:https://www.luogu.org/problemnew/solution/P1850

期望这个东西,本蒟蒻还是没有理解它的线性....

有一句话我觉得总结的挺好的:

期望的计算:如果概率为k的代价为w1,概率为(1-k)的代价为w2,那么期望就是概率乘代价,即 kw1+(1-k)w2

                      对于概率不止两个的,但是各个概率加起来和为1的情况同样适用

期望DP的套路:n个阶段的期望等于每个阶段的期望之和,显然的.    其实,我觉得不是很显然的。。。

总结:

一般的期望DP有成功和失败两种,在阶段进行转移时,当前阶段的失败和成功和上一阶段的成功和失败有关系时,要分开讨论,即分开计算概率再乘以分别的代价。

这道题大概是最简单的期望DP了吧-_-

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define V 305
#define N 2004
#define inf 1e7
#define inf_z 0x3f3f3f3f
using namespace std;

inline int wread(){
    char c=getchar ();int flag=1,wans=0;
    while (c<'0'||c>'9'){if (c=='-') flag=-1;c=getchar ();}
    while (c>='0'&&c<='9'){wans=wans*10+c-'0';c=getchar ();}
    return wans*=flag;
}

int n,m,v,e;
int sav[V][V]; //两点之间的距离 
int a[N],b[N];// a:一开始所在的教室   b:可以申请到的教室 
int dis[V][V]; //两点之间的最短距离
double P[N];//教室更换成功的概率
bool abl2=true;
double dp[2][N][N];

int main (){
	//细节问题:输出小数后两位!!! 
	n=wread();m=wread();v=wread();e=wread();
	//4pts
	if (n==1){
		puts("0.00");return 0;
	}
	memset (sav,inf_z,sizeof sav);
	for (int i=1;i<=n;++i)
		a[i]=wread();
	for (int i=1;i<=n;++i)
		b[i]=wread();
	for (int i=1;i<=n;++i){
		scanf ("%lf",&P[i]);
		if (P[i]!=1.000)	abl2=false;
	}

	for (int i=1;i<=e;++i){
		int q=wread(),z=wread(),w=wread();
		if (q==z)	{sav[q][z]=0;continue;}
		sav[q][z]=min (sav[q][z],w);
		sav[z][q]=sav[q][z];
	}
	//Floyed
	for (int k=1;k<=v;++k){
		for (int i=1;i<=v;++i){
			for (int j=1;j<=v;++j){
				if (i==j)	sav[i][j]=0;
				if (sav[i][j] > sav[i][k] + sav[k][j])
					sav[i][j] = sav[i][k] + sav[k][j],sav[j][i]=sav[i][j];					
			}
		}
	}
	//24pts
	if (m==0){
		double ans=0;
		for (int i=2;i<=n;++i){
			ans+=(double)sav[a[i-1]][a[i]];
		}
		printf("%.2lf\n",(double)ans);
	}
	else {
		//手动memset 
		for (int i=1;i<=n;++i)
			for (int k=0;k<=m;++k)
				dp[0][i][k]=dp[1][i][k]=inf;
		dp[0][1][0]=0.0;
		if (m>=1)	dp[1][1][1]=0.0;
		
		for (int i=2;i<=n;++i){//当前在第i间教室 
			//0
			dp[0][i][0] = dp[0][i-1][0] + (double)sav[a[i-1]][a[i]];
			
			for (int k=1;k<=min (m,i) ;++k){//换了k次教室 
				dp[0][i][k]= min (dp[0][i-1][k]+(double)sav[a[i-1]][a[i]],dp[1][i-1][k]+P[i-1]*(double)sav[b[i-1]][a[i]]+(1.0-P[i-1])*(double)sav[a[i-1]][a[i]]);
				dp[1][i][k]= min (dp[0][i-1][k-1]+P[i]*(double)sav[a[i-1]][b[i]]+(1.0-P[i])*(double)sav[a[i-1]][a[i]], dp[1][i-1][k-1]+  P[i-1]*P[i]*(double)sav[b[i-1]][b[i]] +  P[i-1]*(1-P[i])*(double)sav[b[i-1]][a[i]]  +  (1-P[i-1])*P[i]*(double)sav[a[i-1]][b[i]]  +  (1-P[i-1])*(1-P[i])*(double)sav[a[i-1]][a[i]] ) ;
			}
		}
		double ans=inf;
		for (int k=0;k<=m;++k)
			ans = min (ans, min (dp[0][n][k],dp[1][n][k]));
		printf("%.2lf\n",ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值