【NOIP2016-D1T3】换教室

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hwzzyr/article/details/77199708

【NOIP2016-D1T3】换教室

Description







                                                                                                                       
Solution
emm,最近学习了一下期望。所以就回头把去年noip的题做一下。
这个题目描述很长,虽然说没有大段故事背景这类的废话,但是信息好分散啊。。。所以我们将这道题拆成几个部分分别看。
首先最水的就是Floyd,300的点数看到了颇有一种不做不爽的感觉。然后就是n节课每一节都有两种选择,有m个申请机会调换教室,并且相应的有K[i]的概率通过,最后要求给出申请后最短路径的期望值。
通过数据范围和从前往后的顺序性我们可以先试一试dp管不管用。令f[i][j][0/1]分别表示从第1节课到底i节课申请了j次之后第i节课申请与不申请的最大值(你要是能够断句也就明白了状态了)。
对于f[i][j][0],我们可以从f[i-1][j][0](前一节课没有申请)和f[i-1][j][1](前一节课申请调换)转移。需要注意的是,因为申请第i-1节调换有k[i]的概率通过,所以增加的路程就要乘以概率,这样才能得到期望。同理,将没有申请成功的路程乘以对应的概率,最后的累加才是这一种情况的值。
而f[i][j][1]就稍显复杂,同样是可以从f[i-1][j-1][0]和f[i-1][j-1][1]两个状态转移而来,但是这里我们要同时考虑前一节课申请的概率和本节课的概率,进而在f[i-1][j-1][1]时共有四个小部分。
最后枚举申请次数,取其中的最小值。
送上一组手打数据,拒绝图片样例:
3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1 

CODE

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
inline int read(){
	char c;int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec;
}
int n,m,V,E;
int pp[2005],pn[2005];
double K[2005];
int map[305][305];
inline void Floyed(){
	int i,j,k;
	for(i=1;i<=V;i++)map[i][i]=0;
	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 (i!=j&&j!=k)
                        map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
	return ;
}
double ans=1e18;
double f[2005][2005][2];
int main(){
	n=read();m=read();V=read();E=read();
	for(int i=1;i<=n;i++)pp[i]=read();
	for(int i=1;i<=n;i++)pn[i]=read();
	for(int i=1;i<=n;i++)scanf("%lf",&K[i]);
	memset(map,0x3f,sizeof(map));
	for(int i=1;i<=E;i++){
		int x=read(),y=read(),z=read();
		map[x][y]=map[y][x]=min(map[x][y],z);
	}Floyed();
	for(int i=1;i<=n;i++)
		for(int j=0;j<=m;j++)
		    f[i][j][0]=f[i][j][1]=1e12;
	f[1][0][0]=f[1][1][1]=0;
	for(int i=2;i<=n;i++){
		for(int j=0;j<=m;j++){
			int dpp=map[pp[i-1]][pp[i]],dpn=map[pp[i-1]][pn[i]];
			int dnp=map[pn[i-1]][pp[i]],dnn=map[pn[i-1]][pn[i]];
                        double p1=K[i-1],p2=K[i];
                        if(j<=i-1)
			    f[i][j][0]=min(p1*dnp+(1-p1)*dpp+f[i-1][j][1],
				           dpp+f[i-1][j][0]);
                        if(j)
			    f[i][j][1]=min(f[i-1][j-1][0]+p2*dpn+(1-p2)*dpp,
				           f[i-1][j-1][1]
					   +p1*p2*dnn+p1*(1-p2)*dnp
					   +(1-p1)*p2*dpn+(1-p1)*(1-p2)*dpp);
		}
	}for(int i=0;i<=m&&i<=n;i++)ans=min(ans,min(f[n][i][0],f[n][i][1]));
	printf("%.2lf",ans);
	return 0;
}
定义几个中间变量,减少一下数组嵌套层数。

猜你喜欢

转载自blog.csdn.net/hwzzyr/article/details/77199708