2016 NOIP提高组 换教室 题解

题目传送门

题目大意: 牛牛要上 n n n 天课,第 i i i 天在 c [ i ] c[i] c[i] 号教室,可以申请换至多 m m m 天的教室,第 i i i 天有 p [ i ] p[i] p[i] 的申请成功几率,申请成功的话就可以在 d [ i ] d[i] d[i] 号教室上课,第 i i i 天上完课后要走到第 i + 1 i+1 i+1 天的课室,给出一张图,代表教室之间的通道情况,问申请哪些教室可以使期望路程最短。

题解

首先这个图只有 300 300 300 个点,用 f l o y d floyd floyd 就可以求出任意两点的最短路。

然后就是个简单的 01 01 01 背包,每天只有申请和不申请两种状态,设 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1] 表示前 i i i 天申请了 j j j 次,并且第 i i i 天选择申请( 1 1 1)或不申请( 0 0 0),然后大力从前一天转移过来即可。

转移的时候重点考虑选的天,如果这天被选了,就有 p [ i ] p[i] p[i] 的概率在 c [ i ] c[i] c[i] 教室,有 ( 1 − p [ i ] ) (1-p[i]) (1p[i]) 的概率在 d [ i ] d[i] d[i] 教室,如果第 i i i 天和第 i − 1 i-1 i1 天同时选,那么就有 4 4 4 种情况需要大力分类讨论然后把它们的期望加起来。

(这么丑的dp居然一次AC了qwq可喜可贺)

细节就看代码吧(不算丑qwq):

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 2010
#define inf 999999999.0

int n,m,v,e,c[maxn],d[maxn];
double p[maxn],dp[maxn][maxn][2];
int f[310][310];
void floyd()
{
	for(int i=1;i<=v;i++)
	for(int j=i+1;j<=v;j++)
	f[i][j]=f[j][i]=999999999;
	for(int i=1,x,y,z;i<=e;i++)
	scanf("%d %d %d",&x,&y,&z),f[x][y]=f[y][x]=min(f[x][y],z);
	for(int k=1;k<=v;k++)
	for(int i=1;i<=v;i++)if(i!=k)
	for(int j=1;j<=v;j++)if(j!=i&&j!=k)
	if(f[i][j]>f[i][k]+f[k][j])f[i][j]=f[i][k]+f[k][j];
}
void DP()
{
	for(int i=0;i<=m;i++)dp[1][i][0]=dp[1][i][1]=inf;
	dp[1][0][0]=dp[1][1][1]=0;
	for(int i=2;i<=n;i++)
	for(int j=0;j<=m;j++)
	{
		dp[i][j][0]=min( dp[i-1][j][0]+1.0*f[c[i-1]][c[i]],//我不选,他不选 
		dp[i-1][j][1]+p[i-1]*f[d[i-1]][c[i]]+(1-p[i-1])*f[c[i-1]][c[i]]);//我不选,他选 
		
		if(j>0)dp[i][j][1]=min(//注意要判断一下j是否大于0,如果是0的话i就不能被选
		dp[i-1][j-1][0]+p[i]*f[c[i-1]][d[i]]+(1-p[i])*f[c[i-1]][c[i]],//我选,他不选 
		dp[i-1][j-1][1]+ p[i]*p[i-1]*f[d[i-1]][d[i]]+(1-p[i])*p[i-1]*f[d[i-1]][c[i]]+//我选,他选 
		p[i]*(1-p[i-1])*f[c[i-1]][d[i]]+(1-p[i])*(1-p[i-1])*f[c[i-1]][c[i]]);
		else dp[i][j][1]=inf;
	}
}

int main()
{
	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",&p[i]);
	floyd();DP();
	double ans=999999999;
	for(int i=0;i<=m;i++)ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
	printf("%.2lf",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值