[NOIP 2016] 换教室(期望DP)

题目

题 目 传 送 门 \color{red}{题目传送门}
在这里插入图片描述
在这里插入图片描述
输入输出样例
输入样例

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

输出样例

2.80

在这里插入图片描述

题解

  • 这题真的水,第一次写DP一眼看出方程式qaq
  • 声明:
    1. c [ i ] c[i] c[i]为原来上课的教室, d [ i ] d[i] d[i]为换后的教室
    2. p [ i ] p[i] p[i]表示换成功的概率
    3. r [ i ] [ j ] r[i][j] r[i][j]表示 i , j i,j i,j两点之间的距离
    4. f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示当前选到了第 i i i个时间段,已经换了 j j j次教室,当前时间段是否换教室( k = 0 k = 0 k=0 1 1 1 0 0 0表示不选, 1 1 1表示选)所获得的最小期望
    5. { p [ i − 1 ] ∗ r [ d [ i − 1 ] ] [ c [ i ] ] i − 1 换 课 成 功 ( 1 − p [ i − 1 ] ) ∗ r [ c [ i − 1 ] ] [ c [ i ] ] i − 1 换 课 失 败 \begin{cases}p[i-1]*r[d[i-1]][c[i]] & i-1换课成功\\ (1-p[i-1])*r[c[i-1]][c[i]] & i-1换课失败\end{cases} {p[i1]r[d[i1]][c[i]](1p[i1])r[c[i1]][c[i]]i1i1
    其他情况同理可得
  • 状态转移
    ∙ \bullet 若当前没有换教室
    f [ i ] [ j ] [ 0 ] = { f [ i − 1 ] [ j ] [ 1 ] + p [ i − 1 ] ∗ r [ d [ i − 1 ] ] [ c [ i ] ] + ( 1 − p [ i − 1 ] ) ∗ ( r [ c [ i − 1 ] ] [ c [ i ] ] ) f [ i − 1 ] [ j ] [ 0 ] + r [ c [ i − 1 ] ] [ c [ i ] ] f[i][j][0] = \begin{cases}f[i-1][j][1]+p[i-1]*r[d[i-1]][c[i]]+(1-p[i-1])*(r[c[i-1]][c[i]]) \\ f[i-1][j][0]+r[c[i-1]][c[i]]\end{cases} f[i][j][0]={f[i1][j][1]+p[i1]r[d[i1]][c[i]]+(1p[i1])(r[c[i1]][c[i]])f[i1][j][0]+r[c[i1]][c[i]]
    ∙ \bullet 若当前换教室
    a = p [ i − 1 ] ∗ p [ i ] ∗ r [ d [ i − 1 ] ] [ d [ i ] ] a= p[i-1] * p[i] * r[d[i-1]][d[i]] a=p[i1]p[i]r[d[i1]][d[i]] i − 1 i-1 i1 i i i都换课成功
    b = ( 1 − p [ i − 1 ] ) ∗ p [ i ] ∗ r [ c [ i − 1 ] ] [ d [ i ] ] b=(1 - p[i-1]) * p[i] * r[c[i-1]][d[i]] b=(1p[i1])p[i]r[c[i1]][d[i]] i − 1 i-1 i1个失败第 i i i个成功
    c = p [ i − 1 ] ∗ ( 1 − p [ i ] ) ∗ r [ d [ i − 1 ] ] [ c [ i ] ] c=p[i-1] * (1 - p[i]) * r[d[i-1]][c[i]] c=p[i1](1p[i])r[d[i1]][c[i]] i − 1 i-1 i1个成功第 i − 1 i-1 i1个失败
    d = ( 1 − p [ i − 1 ] ) ∗ ( 1 − p [ i ] ) ∗ r [ c [ i − 1 ] ] [ c [ i ] ] d=(1 - p[i-1]) * (1 - p[i]) * r[c[i-1]][c[i]] d=(1p[i1])(1p[i])r[c[i1]][c[i]] i − 1 i-1 i1和第 i i i个都失败
    f [ i ] [ j ] [ 1 ] = { f [ i − 1 ] [ j − 1 ] [ 1 ] + a + b + c + d f [ i − 1 ] [ j − 1 ] [ 0 ] + p [ i ] ∗ r [ c [ i − 1 ] ] [ d [ i ] ] + ( 1 − p [ i ] ) ∗ r [ c [ i − 1 ] ] [ c [ i ] ] f[i][j][1] = \begin{cases}f[i-1][j-1][1] + a + b +c + d \\ f[i-1][j-1][0] + p[i] * r[c[i-1]][d[i]] + (1 - p[i]) * r[c[i-1]][c[i]]\end{cases} f[i][j][1]={f[i1][j1][1]+a+b+c+df[i1][j1][0]+p[i]r[c[i1]][d[i]]+(1p[i])r[c[i1]][c[i]]

c o d e code code

#include <bits/stdc++.h> 
using namespace std; 
const int maxn = 2e3 + 100; 
const int inf = 0x3f3f3f3f; 

template <typename T> 
inline void read(T &s) {
	s = 0; 
	T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
	while (isdigit(ch)) { s = (s<<1) + (s<<3) + (ch^48); ch = getchar(); } 
	s *= w; 
}

int n, m, v, e; 
int c[maxn], d[maxn]; 
int r[maxn][maxn]; 
double f[maxn][maxn][2];
double p[maxn]; 

inline void floyd() {
	for (int k = 1; k <= v; ++k) 
		for (int i = 1; i <= v; ++i) 
			for (int j = 1; j < v; ++j) 
				if (r[i][j] > r[i][k] + r[k][j])  
					r[j][i] = r[i][j] = r[i][k] + r[k][j]; 
}

inline void dp() {
	for (int i = 1; i <= n; ++i)
		for (int j = 0; j <= m; ++j)
			for (int k = 0; k <= 1; ++k) 
				f[i][j][k] = 123456789.66; 
	f[1][0][0] = 0.00; 
	f[1][1][1] = 0.00; 
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			double x = f[i-1][j][1] + p[i-1] * r[d[i-1]][c[i]] + (1 - p[i-1]) * r[c[i-1]][c[i]]; 
			double y = f[i-1][j][0] + r[c[i-1]][c[i]]; 
			f[i][j][0] = min(x, y); 
			if (j) {
				x = f[i-1][j-1][1] + p[i-1] * p[i] * r[d[i-1]][d[i]] + (1 - p[i-1]) * p[i] * r[c[i-1]][d[i]] + p[i-1] * (1 - p[i]) * r[d[i-1]][c[i]] +  (1 - p[i-1]) * (1 - p[i]) * r[c[i-1]][c[i]]; 
				y = f[i-1][j-1][0] + p[i] * r[c[i-1]][d[i]] + (1 - p[i]) * r[c[i-1]][c[i]]; 
				f[i][j][1] = min(x, y); 
			}
		}
	}
}

int main() {
	read(n), read(m), read(v), read(e); 
	for (int i = 1; i <= n; ++i) read(c[i]); 
	for (int i = 1; i <= n; ++i) read(d[i]); 
	for (int i = 1; i <= n; ++i) scanf("%lf", &p[i]); 
	for (int i = 1; i <= v; ++i) 
		for (int j = 1; j < i; ++j) 
			r[i][j] = r[j][i] = inf;  
	for (int i = 1; i <= e; ++i) {
		int x, y, z; 
		read(x), read(y), read(z); 
		r[x][y] = min(r[x][y], z); 
		r[y][x] = r[x][y]; 
	}	
	floyd(); 
	dp(); 
	double ans = 123456789.66; 
	for (int i = 0; i <= m; ++i) 
		for (int j = 0; j <= 1; ++j) 
			ans = min(ans, f[n][i][j]); 
	printf("%.2lf\n", ans); 
	return 0; 
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值