【题解】LuoGu1850:换教室

原题传送门
期望dp
状态: d p i , j , 0 / 1 dp_{i,j,0/1} dpi,j,0/1表示到第 i i i个时间点,共计请求 j j j次,当前这一个时间点是否请求的答案
预处理: v < = 300 v<=300 v<=300直接 f l o y d floyd floyd预处理最短路
转移:
d p i , j , 0 = m i n ( d p i − 1 , j , 0 + w c i − 1 , c i , d p i − 1 , j , 1 + w c i − 1 , c i ∗ ( 1 − k i − 1 ) + w d i − 1 , c i ∗ k i ) dp_{i,j,0}=min(dp_{i-1,j,0}+w_{c_{i-1},{c_i}},dp_{i-1,j,1}+w_{c_{i-1},c_i}*(1-k_{i-1})+w_{d_{i-1},c_i}*k_i) dpi,j,0=min(dpi1,j,0+wci1,ci,dpi1,j,1+wci1,ci(1ki1)+wdi1,ciki)
d p i , j , 1 = m i n ( d p i − 1 , j − 1 , 0 + w c i − 1 , c i ∗ ( 1 − k i ) + w c i − 1 , d i ∗ k i , d p i − 1 , j − 1 , 1 + w c i − 1 , c i ∗ ( 1 − k i − 1 ) ∗ ( 1 − k i ) + w c i − 1 , d i ∗ ( 1 − k i − 1 ) ∗ k i + w d i − 1 , c i − 1 ∗ k i − 1 ∗ ( 1 − k i ) + w d i − 1 , d i ∗ k i − 1 ∗ k i ) dp_{i,j,1}=min(dp_{i-1,j-1,0}+w_{c_{i-1},c_i}*(1-k_i)+w_{c_{i-1},d_i}*k_i,dp_{i-1,j-1,1}+w_{c_{i-1},c_i}*(1-k_{i-1})*(1-k_i)+w_{c_{i-1},d_i}*(1-k_{i-1})*k_i+w_{d_{i-1},c_{i-1}}*k_{i-1}*(1-k_i)+w_{d_{i-1},d_i}*k_{i-1}*k_i) dpi,j,1=min(dpi1,j1,0+wci1,ci(1ki)+wci1,diki,dpi1,j1,1+wci1,ci(1ki1)(1ki)+wci1,di(1ki1)ki+wdi1,ci1ki1(1ki)+wdi1,diki1ki)

Code:

#include <bits/stdc++.h>
#define maxn 2010
using namespace std;
double dp[maxn][maxn][2], k[maxn];
int n, m, v, e, c[maxn], d[maxn], w[maxn][maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

int main(){
	n = read(), m = read(), v = read(), e = read();
	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]);
	for (int i = 1; i <= v; ++i)
		for (int j = 1; j <= v; ++j)
			if (i != j) w[i][j] = 1e9;
	for (int i = 1; i <= e; ++i){
		int x = read(), y = read(), z = read();
		w[x][y] = min(w[x][y], z), w[y][x] = w[x][y];
	}
	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 (j != k && i != j)
					w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
	for (int i = 0; i <= n; ++i)
		for (int j = 0; j <= m; ++j) dp[i][j][0] = dp[i][j][1] = 1e9;
	dp[1][0][0] = dp[1][1][1] = 0;
	for (int i = 2; i <= n; ++i){
		dp[i][0][0] = dp[i - 1][0][0] + w[c[i - 1]][c[i]];
		for (int j = 1; j <= min(m, i); ++j){
			int A = c[i - 1], B = d[i - 1], C = c[i], D = d[i];
			dp[i][j][0] = min(dp[i - 1][j][0] + w[A][C], dp[i - 1][j][1] + w[A][C] * (1 - k[i - 1]) + w[B][C] * k[i - 1]),
			dp[i][j][1] = min(dp[i - 1][j - 1][0] + w[A][C] * (1 - k[i]) + w[A][D] * k[i],
			 dp[i - 1][j - 1][1] + w[A][C] * (1 - k[i - 1]) * (1 - k[i]) + w[B][C] * k[i - 1] * (1 - k[i]) + w[A][D] * (1 - k[i - 1]) * k[i] + w[B][D] * k[i - 1] * k[i]);
		}
	}
	double ans = 1e9;
	for (int i = 0; i <= m; ++i) ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
	printf("%.2lf\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值