[NOIP2016 提高组] 换教室

前言

被绕进去了,瞄了一眼题解,发现状态有点问题。

当然转移还是可以自己写的。

题目

洛谷

讲解

首先这道题很容易陷入这样一个误区:我dp要转移,必须要知道前一个位置在哪里,那么顺理成章地,定义状态 \(dp_{i,j,0/1}\) 表示第 \(i\) 个课程,申请了 \(j\) 次,现在在 \(0/1\) 位置的最小期望路程。

但是这样新的问题就出现了,你的上一步是哪一个位置呢?或者说你用什么来更新当前状态呢?用最小值?但是你不知道你上一步到底有没有申请成功(申请是一次性全部提交),而且我甚至无法判断当前状态是否应该申请换教室。

所以我们换个思路,申请是独立的,那么用申请来当状态就没有后效性了!

\(dp_{i,j,0/1}\) 表示第 \(i\) 个课程,申请了 \(j\) 次,现在在是否申请换教室的最小期望路程,这样一来转移就简单多了。

当然 \(\tt Floyd\) 预处理最短路就不用多说了吧。

时间复杂度 \(O(nm+V^3)\)

代码

具体转移可以看代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 2005;
const int INF = 0x3f3f3f3f;
int n,m,V,E;
int c[MAXN],d[MAXN];
double p[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int e[MAXN][MAXN];
double dp[MAXN][MAXN][2]; 

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	memset(e,0x3f,sizeof(e));
	n = Read(); m = Read(); V = Read(); E = Read();
	for(int i = 1;i <= V;++ i) e[i][i] = 0;
	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",&p[i]);
	for(int i = 1;i <= E;++ i)
	{
		int u = Read(),v = Read();
		e[u][v] = Min(0ll+e[u][v],Read());
		e[v][u] = e[u][v];
	}
	for(int k = 1;k <= V;++ k)
		for(int i = 1;i <= V;++ i)
			for(int j = 1;j <= V;++ j)
				e[i][j] = Min(e[i][j],e[i][k]+e[k][j]);
	for(int i = 1;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] = dp[1][1][0] = 0;
	for(int i = 2;i <= n;++ i)
		for(int j = 0;j <= m;++ j)
		{
			dp[i][j][0] = Min(dp[i-1][j][0]+e[c[i-1]][c[i]],dp[i-1][j][1]+p[i-1]*e[d[i-1]][c[i]]+(1-p[i-1])*e[c[i-1]][c[i]]);
			if(j) 
			{
				dp[i][j][1] = dp[i-1][j-1][0]+p[i]*e[c[i-1]][d[i]]+(1-p[i])*e[c[i-1]][c[i]];
				double val = 0;
				for(int k = 0;k < 2;++ k)
					for(int l = 0;l < 2;++ l)
					{
						double fk = 1;
						int u,v;
						if(!k) fk *= 1-p[i-1],u = c[i-1];
						else fk *= p[i-1],u = d[i-1];
						
						if(!l) fk *= 1-p[i],v = c[i];
						else fk *= p[i],v = d[i];
						val += fk * e[u][v];
					}
				dp[i][j][1] = Min(dp[i][j][1],dp[i-1][j-1][1]+val);
			}
		}
	double ans = INF;
	for(int i = 0;i <= m;++ i) ans = Min(ans,Min(dp[n][i][0],dp[n][i][1]));
	printf("%.2f\n",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/15564046.html