Vijos2005 换教室(最短路+期望DP)

vijos 2005 换教室(最短路+期望DP)

题意

给定图上一条路径(n个点),可以将这条路径上的一个点(ci)修改为另一个指定点(di)(最多修改点数不能超过m),每一个点被成功修改的概率为ki,求出路径长度的最小期望值。

思路

因为要求最小期望值,所以两个点的距离只保留最小的一定最优。要实现这个目的做一遍最短路即可。
再思考一个问题: 假设已经知道修改m个点,怎么确定最优方案?不难想到枚举第m个点的位置pos,在[1,pos-1]中寻找m-1个点的最小期望值,再利用概率论的知识求解。同理,m-1个点的最小期望值也可以这样求。
然后想到DP,前两维根据分析推出是pos和m,第三维考虑如何根据前面的状态值求解:我们在得到pos-1的最小期望值时,还需要知道从pos-1到pos的距离才可以得到pos的最小期望值,但是pos-1我们不知道是否修改过,也就无法直接求出pos-1到pos的距离,那么很明显,第三维就是表示第pos个点是否申请修改(不一定是成功修改了),如何求期望呢?就利用每一个事件发生的概率×每一个事件发生的结果的和即可。(即pos和pos-1的是否成功修改情况和这种情况下对应的结果)

AC代码

#include<bits/stdc++.h>
using namespace std;
const int Maxv=305,Maxn=2005;
typedef double db;
#define dbmax 1.9*1e9
int dis[Maxv][Maxv];
int DIS[Maxn][5];//专门存储从i-1到i的距离 根据c和d不同有四种情况
int n,m,v,e;
int c[Maxn],d[Maxn];
double k[Maxn],f[Maxn][Maxn][2];
double fmin(double a,double b){
	if(a<b)return a;
	else return b;
}
void floyed(){
	for(int i=1;i<=v;i++)dis[i][i]=0;
	for(int k=1;k<=v;k++){
		for(int i=1;i<=v;i++){
			for(int j=1;j<=v;j++){
					if(dis[i][k]+dis[k][j]<dis[i][j]){
						dis[i][j]=dis[i][k]+dis[k][j];
					}
			}
		}
	}
	/*for(int i=1;i<=v;i++){
		for(int j=1;j<=v;j++){
			if(i==j)continue;
			printf("%d to %d is %d\n",i,j,dis[i][j]);
		}
	}*/
	
}
double work(){
	for(int i=2;i<=n;i++){
		DIS[i][1]=dis[c[i-1]][c[i]];//表示c[i-1]到c[i]的距离
		DIS[i][2]=dis[c[i-1]][d[i]];//表示c[i-1]到d[i]的距离
		DIS[i][3]=dis[d[i-1]][c[i]];//表示d[i-1]到c[i]的距离
		DIS[i][4]=dis[d[i-1]][d[i]];//表示d[i-1]到d[i]的距离
	}
	//特殊处理下 因为有些状态是不可能的 (比如选的教室比当前教室还多
	memset(f,0x7f,sizeof(f));
	f[1][1][1]=f[1][0][0]=0;
	for(int i=2;i<=n;i++){
		f[i][0][0]=f[i-1][0][0]+DIS[i][1]; 
		for(int j=1;j<=min(i,m);j++){
			f[i][j][0]=fmin(f[i-1][j][0]+DIS[i][1],f[i-1][j][1]+DIS[i][1]*(1.0-k[i-1])+DIS[i][3]*k[i-1]);
			double f1,f2;
			f1=f[i-1][j-1][0]+DIS[i][1]*(1.0-k[i])+DIS[i][2]*k[i];
			f2=f[i-1][j-1][1]+DIS[i][1]*(1.0-k[i])*(1.0-k[i-1])+DIS[i][2]*(1.0-k[i-1])*k[i]+DIS[i][3]*k[i-1]*(1.0-k[i])+DIS[i][4]*k[i-1]*k[i];
			f[i][j][1]=fmin(f1,f2);
		}
	}
	db ans=dbmax;
	for(int i=0;i<=m;i++){
		//printf("f[%d][%d][0]=%.2f\n",n,i,f[n][i][0]);
		//printf("f[%d][%d][1]=%.2f\n",n,i,f[n][i][1]);
		ans=fmin(ans,f[n][i][0]);
		ans=fmin(ans,f[n][i][1]);
	}
	return ans;
}
void readin(){
	memset(dis,0x3f,sizeof(dis));
	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]);
	for(int i=1;i<=e;i++){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		dis[a][b]=min(dis[a][b],w);
		dis[b][a]=min(dis[b][a],w);
	}
	floyed();
}
int main(){
	readin();
	printf("%.2lf",work());
	return 0;
}

结语

恩…转移方程推理过程有空再贴吧(不会

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值