2020.01.20日常总结

洛 谷 P 1850        换 教 室 \color{green}{洛谷P1850\ \ \ \ \ \ 换教室} P1850      

【 题 意 】 : \color{blue}{【题意】:}
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
【 思 路 】 : \color{blue}{【思路】:} 好冗长的题目啊……

这是一道 概 率 d p \color{red}{概率dp} dp的题目。所谓的概率dp,大家可以直观的理解为用以计算概率的 d p dp dp。它最大的特点是数学性强(即需要用到很多的数学公式)。

回到本题,我们根据 d p dp dp的一般步骤来解答它。

第一步是检验本题是否可以 d p dp dp解决。即检查本题是否具有无后效性等等的 d p dp dp特性。此步很重要,但是因为篇幅,这里略去,仅仅告诉读者结论,本题可以用 d p dp dp

第二步是设计状态。状态的设计一般需要依据数据范围来确定。对于本题,我们记 d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]表示考虑到第 i i i个时间段,共换了 j j j次教室,其中如果第三维是 0 0 0,表示当前不需要换教室,是 1 1 1表示需要换教室; f [ i ] [ j ] f[i][j] f[i][j]表示从 i i i j j j的最短路径,可以Floyd求出。

第三步是考虑转移。概率 d p dp dp的又一大特性是它常常需要 分 类 讨 论 \color{orange}{分类讨论}

C 1 = c [ i − 1 ] , C 2 = d [ i − 1 ] , C 3 = c [ i ] , C 4 = d [ i ] C1=c[i-1],C2=d[i-1],C3=c[i],C4=d[i] C1=c[i1],C2=d[i1],C3=c[i],C4=d[i],则

  • d p [ i ] [ j ] [ 0 ] = m i n ( d p [ i ] [ j ] [ 0 ] , m i n ( d p [ i − 1 ] [ j ] [ 0 ] + f [ C 1 ] [ C 3 ] , d p [ i − 1 ] [ j ] [ 1 ] + f [ C 1 ] [ C 3 ] × ( 1 − k [ i − 1 ] ) + f [ C 2 ] [ C 3 ] × k [ i − 1 ] ) ) dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+f[C1][C3],dp[i-1][j][1]+f[C1][C3]\times (1-k[i-1])+f[C2][C3]\times k[i-1])) dp[i][j][0]=min(dp[i][j][0],min(dp[i1][j][0]+f[C1][C3],dp[i1][j][1]+f[C1][C3]×(1k[i1])+f[C2][C3]×k[i1]))
  • d p [ i ] [ j ] [ 1 ] = m i n ( d p [ i ] [ j ] [ 1 ] , m i n ( d p [ i − 1 ] [ j − 1 ] [ 0 ] + f [ C 1 ] [ C 3 ] × ( 1 − k [ i ] ) + f [ C 1 ] [ C 4 ] × k [ i ] , d p [ i − 1 ] [ j − 1 ] [ 1 ] + f [ C 2 ] [ C 4 ] × k [ i − 1 ] × k [ i ] + f [ C 2 ] [ C 3 ] × k [ i − 1 ] × ( 1 − k [ i ] ) + f [ C 1 ] [ C 4 ] × ( 1 − k [ i − 1 ] ) × k [ i ] + f [ C 1 ] [ C 3 ] × ( 1 − k [ i − 1 ] ) × ( 1 − k [ i ] ) ) ) dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+f[C1][C3]\times (1-k[i])+f[C1][C4]\times k[i],dp[i-1][j-1][1]+f[C2][C4]\times k[i-1]\times k[i]+f[C2][C3]\times k[i-1]\times (1-k[i])+f[C1][C4]\times (1-k[i-1])\times k[i]+f[C1][C3]\times (1-k[i-1]) \times (1-k[i]))) dp[i][j][1]=min(dp[i][j][1],min(dp[i1][j1][0]+f[C1][C3]×(1k[i])+f[C1][C4]×k[i],dp[i1][j1][1]+f[C2][C4]×k[i1]×k[i]+f[C2][C3]×k[i1]×(1k[i])+f[C1][C4]×(1k[i1])×k[i]+f[C1][C3]×(1k[i1])×(1k[i])))

d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0]的转移为例。有一种方案是第 i − 1 i-1 i1秒不换教室,两次都不换教室,则答案为 d p [ i − 1 ] [ j ] [ 0 ] + f [ C 1 ] [ C 3 ] dp[i-1][j][0]+f[C1][C3] dp[i1][j][0]+f[C1][C3]。令一种方案为第 i − 1 i-1 i1秒换教室,则又有两种方案:一是从 c [ i − 1 ] c[i-1] c[i1] d [ i − 1 ] d[i-1] d[i1]成功,则贡献为 f [ C 2 ] [ C 3 ] × k [ i − 1 ] f[C2][C3]\times k[i-1] f[C2][C3]×k[i1];二是从 c f [ i − 1 ] cf[i-1] cf[i1] d [ i − 1 ] d[i-1] d[i1]不成功,既然从 c [ i − 1 ] c[i-1] c[i1] d [ i − 1 ] d[i-1] d[i1]成功的概率为 k [ i − 1 ] k[i-1] k[i1],那么不成功的概率为 1 − k [ i − 1 ] 1-k[i-1] 1k[i1],因为两件事不同时发生,则总贡献为 f [ C 1 ] [ C 3 ] × ( 1 − k [ i − 1 ] ) f[C1][C3]\times (1-k[i-1]) f[C1][C3]×(1k[i1])。所以总答案为 d p [ i − 1 ] [ j ] [ 1 ] + f [ C 1 ] [ C 3 ] × ( 1 − k [ i − 1 ] ) + f [ C 2 ] [ C 3 ] × k [ i − 1 ] dp[i-1][j][1]+f[C1][C3] \times (1-k[i-1])+f[C2][C3]\times k[i-1] dp[i1][j][1]+f[C1][C3]×(1k[i1])+f[C2][C3]×k[i1] d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0]就在两种答案间取较小值即可。

d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]的转移类似,请读者思考为什么 d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]的转移是那样的。

一般地,如果考虑转移方程时出现困难,有可能是自己考虑不够深入。也有可能是状态的定义出错,这时,我们需要倒回第 2 2 2步,重新思考。

【 代 码 】 : \color{blue}{【代码】:}

int f[310][310],n,m,v,e;
double dp[2010][2010][3];
//f[i][j]:从i到j的最短路径,可以Floyd求出 
//dp[i][j][1]:考虑到第i个时间段,共换了j次教室,当前也换教室的答案
//dp[i][j][0]:考虑到第i个时间段,共换了j次教室,当前不换教室的答案
inline void floyd_algorithm(){
	for(int k=1;k<=v;k++)
		for(int i=1;i<=v;i++)
			if (i!=k)
				for(int j=1;j<=v;j++)
					f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
double k[2010],ans;
int c[2010],d[2010];
const double inf=1e17;
int main(){
	freopen("t1.in","r",stdin);
	scanf("%d%d%d%d",&n,&m,&v,&e);
	for(int i=1;i<=n;i++)
		scanf("%d",&c[i]);
	for(int i=1;i<=n;i++)
		scanf("%d",&d[i]);
	for(int i=1;i<=n;i++)
		scanf("%lf",&k[i]);
	memset(f,63,sizeof(f));
	for(int i=1;i<=e;i++){
		register int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		f[u][v]=f[v][u]=min(f[u][v],w);
	}
	floyd_algorithm();ans=inf;
	for(int i=1;i<=v;i++)
		f[i][i]=f[i][0]=f[0][i]=0;
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			dp[i][j][0]=dp[i][j][1]=inf;
	dp[1][0][0]=dp[1][1][1]=0;
	for(int i=2;i<=n;i++){
		int C1=c[i-1],C2=d[i-1],C3=c[i],C4=d[i];
		dp[i][0][0]=dp[i-1][0][0]+f[C1][C3];
		for(int j=1;j<=min(i,m);j++){
			dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+f[C1][C3],dp[i-1][j][1]+f[C1][C3]*(1-k[i-1])+f[C2][C3]*k[i-1]));
			dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+f[C1][C3]*(1-k[i])+f[C1][C4]*k[i],dp[i-1][j-1][1]+f[C2][C4]*k[i-1]*k[i]+f[C2][C3]*k[i-1]*(1-k[i])+f[C1][C4]*(1-k[i-1])*k[i]+f[C1][C3]*(1-k[i-1])*(1-k[i])));
		}
	}
	for(int i=0;i<=m;i++)
		ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
	printf("%.2lf",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值