NOIP2016 换教室

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

D1T3换教室

做过且仅做过的一道期望DP,感觉DP不是很难写,就是细节贼多还容易弄混(论36pts -> 100pts的惨痛经历)
思路:
d p [ i ] [ j ] [ 0 / 1 ] dp[i][j][0/1] 表示:处理到了序列第 i i 个元素,使用了 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)));
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)));
其中 v a l ( i , i 1 , c 1 , c 2 ) val(i,i-1,c1,c2) 表示:当前处理到第 i i 个节点,考虑当前节点以及它前一个节点是否被申请。 0 1 c 1 c 2 (0:申请,1:不申请;c1:当前节点,c2:前一个节点)
代码:
注意 d p dp 的初始化, f l o y d floyd 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;
}
发布了37 篇原创文章 · 获赞 11 · 访问量 1929

猜你喜欢

转载自blog.csdn.net/weixin_42750325/article/details/102962088
今日推荐