NOIP2016 换教室

感觉这道题代码细节令人智熄

D1T3换教室

做过且仅做过的一道期望DP,感觉DP不是很难写,就是细节贼多还容易弄混(论36pts -> 100pts的惨痛经历)
思路:
d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] dp[i][j][0/1]表示:处理到了序列第 i i i个元素,使用了 j j j次申请机会,当前序列申请还是不申请时(0:申请,1:不申请),按照题意走过的最小期望长度。
于是可以简单的设计出下列转移方程:
d p [ i ] [ j ] [ 0 ] = m i n ( d p [ i ] [ j ] [ 0 ] , m i n ( d p [ i − 1 ] [ j ] [ 0 ] + v a l ( i , i − 1 , 0 , 0 ) , d p [ i − 1 ] [ j ] [ 1 ] + v a l ( i , i − 1 , 0 , 1 ) ) ) ; dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+val(i,i-1,0,0),dp[i-1][j][1]+val(i,i-1,0,1))); dp[i][j][0]=min(dp[i][j][0],min(dp[i1][j][0]+val(i,i1,0,0),dp[i1][j][1]+val(i,i1,0,1)));
d p [ i ] [ j ] [ 1 ] = m i n ( d p [ i ] [ j ] [ 1 ] , m i n ( d p [ i − 1 ] [ j − 1 ] [ 0 ] + v a l ( i , i − 1 , 1 , 0 ) , d p [ i − 1 ] [ j − 1 ] [ 1 ] + v a l ( i , i − 1 , 1 , 1 ) ) ) ; dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+val(i,i-1,1,0),dp[i-1][j-1][1]+val(i,i-1,1,1))); dp[i][j][1]=min(dp[i][j][1],min(dp[i1][j1][0]+val(i,i1,1,0),dp[i1][j1][1]+val(i,i1,1,1)));
其中 v a l ( i , i − 1 , c 1 , c 2 ) val(i,i-1,c1,c2) val(i,i1,c1,c2)表示:当前处理到第 i i i个节点,考虑当前节点以及它前一个节点是否被申请。 ( 0 : 申 请 , 1 : 不 申 请 ; c 1 : 当 前 节 点 , c 2 : 前 一 个 节 点 ) (0:申请,1:不申请;c1:当前节点,c2:前一个节点) 01c1c2
代码:
注意 d p dp dp的初始化, f l o y d floyd floyd v v v才是节点数,要建反向边

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N=2050;
const double inf=1e17;
int n,m,v,e,c[N],d[N],dis[N][N];
double dp[N][N][2];
double k[N];
void floyd(){for(int k=1;k<=v;k++)for(int i=1;i<=v;i++)for(int j=1;j<=v;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}
double val(int x,int y,int c1,int c2){//c1第i个位置 c2第i-1个位置  
	double ans=0;
	if(c1==0&&c2==0){//原来两个教室之间的路径期望长度 
		return dis[c[x]][c[y]];
	}
	else if(c1==0&&c2==1){//对这个申请成功/不成功的概率*成功/不成功的路径长度,求和 
		ans+=k[y]*dis[d[y]][c[x]];
		ans+=(1-k[y])*dis[c[x]][c[y]];
		return ans;
	}
	else if(c1==1&&c2==0){//对这个申请成功/不成功的概率*成功/不成功的路径长度,求和 
		ans+=k[x]*dis[d[x]][c[y]];
		ans+=(1-k[x])*dis[c[x]][c[y]];
		return ans;
	}
	else if(c1==1&&c2==1){//四种情况分别按概率加权求和  
		ans+=k[x]*k[y]*dis[d[x]][d[y]];
		ans+=k[x]*(1-k[y])*dis[d[x]][c[y]];
		ans+=(1-k[x])*(k[y])*dis[c[x]][d[y]];
		ans+=(1-k[x])*(1-k[y])*dis[c[x]][c[y]];
		return ans;
	}
}
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
int main(){
	n=read(),m=read(),v=read(),e=read();memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=n;i++)c[i]=read();
	for(int i=1;i<=n;i++)d[i]=read();
	for(int i=1;i<=n;i++)scanf("%lf",&k[i]);
	int x,y,z;
	for(int i=1;i<=e;i++){
		x=read(),y=read(),z=read();
		dis[x][y]=min(dis[x][y],z);
		dis[y][x]=dis[x][y];
	}
	floyd();
	for(int i=0;i<=v;i++) dis[i][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++)
		dp[i][0][0]=dis[c[i-1]][c[i]]+dp[i-1][0][0];
	for(int i=2;i<=n;i++){
		for(int j=1;j<=m;j++){
			dp[i][j][0]=min(dp[i][j][0],min(dp[i-1][j][0]+val(i,i-1,0,0),dp[i-1][j][1]+val(i,i-1,0,1)));
			dp[i][j][1]=min(dp[i][j][1],min(dp[i-1][j-1][0]+val(i,i-1,1,0),dp[i-1][j-1][1]+val(i,i-1,1,1)));
		}
	}
	double ans=99999999.0;
	for(int i=0;i<=m;i++)
		ans=min(dp[n][i][0],min(ans,dp[n][i][1]));
	printf("%.2lf",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值