NOIP2016 换教室
这道题当时看了一会觉得是DP……甚至连状态都想出来了,但是因为数学太差不知道怎么转移。
按照DP的思路想,我们很容易想到用dp[i][j]表示枚举前i个教室,其中换了j个教室所耗费的最小体力值。一开始这么想后来发现根本没法转移……因为我们是需要知道上一次的教室的,于是再加一维0/1,表示当前这次是否换教室。
如何转移?这里涉及到期望的问题。对于当前的决策,我们有以下情况:
1.当前没申请而且上一次也没申请
2.当前没申请但是上一次申请了
3.当前申请了而且上一次也申请了
其中第一种情况可以直接处理,就是每次加上两点之间的路程就可以。
后面的就比较复杂,因为你申请了就要考虑自己能否成功。这里就有期望的一个重要概念,当前的期望对于下一次的期望是没有影响的,换言之,对于每个当前的决策,你只要算出来所有的情况中,每种情况的权值乘上这种情况的期望,并且把他们求和就可以。后面的决策期望与本次无关。这样计算和你先把所有情况的路径总长计算出来再乘以所有期望是一样的。(我数学太菜了想不出来)
这样就可以进行DP。对于上面所说的第二种情况,我们考虑上一次申请成功/失败的情况并且全部加上,第三种情况就进行自由组合,考虑全部四种情况即可。
DP方程特别长……代码里写。
之后在预处理的时候,因为点数只有300所以使用floyd处理每两点之间的最短路,注意在预处理的时候dis[i][i],dis[i][0],dis[0][i]要清0,剩下的除了已经被赋值的就设为极大值。
然后对于dp数组,初始dp[1][0][0] = dp[1][1][1] = 0,这个很显然,其他全部赋值为极大值。然后对于每个dp[i][0][0]的情况(第一种情况)单独计算即可。
本题的细节还是相当多的……也容易写错
看代码。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 50005; const int N = 250005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,m,v,e,ecnt,dis[305][305],c[2005],d[2005],head[2005],x,y,z; double k[2005],dp[2005][2005][2],INF = 99999999,ans = 999999999; void floyd() { rep(k,1,v) rep(i,1,v) rep(j,1,v) dis[i][j] = min(dis[i][k] + dis[k][j],dis[i][j]); } void init() { rep(i,1,v) dis[i][i] = dis[i][0] = dis[0][i] = 0; rep(i,0,n) rep(j,0,m) dp[i][j][0] = dp[i][j][1] = INF; } int main() { memset(dis,127/3,sizeof(dis)); n = read(),m = read(),v = read(),e = read(); rep(i,1,n) c[i] = read(); rep(i,1,n) d[i] = read(); rep(i,1,n) scanf("%lf",&k[i]); rep(i,1,e) x = read(),y = read(),z = read(),dis[x][y] = dis[y][x] = min(z,dis[x][y]); floyd();init(); dp[1][0][0] = dp[1][1][1] = 0; rep(i,2,n) { dp[i][0][0] = dp[i-1][0][0] + dis[c[i-1]][c[i]]; rep(j,1,min(i,m)) { dp[i][j][0] = min(dp[i][j][0],min(dp[i-1][j][0]+dis[c[i-1]][c[i]],dp[i-1][j][1] + dis[d[i-1]][c[i]] * k[i-1] + dis[c[i-1]][c[i]] * (1 - k[i-1]))); double g = dp[i-1][j-1][1] + dis[c[i-1]][c[i]] * (1-k[i-1]) * (1-k[i]) + dis[c[i-1]][d[i]] * (1-k[i-1]) * k[i] + dis[d[i-1]][c[i]] * k[i-1] * (1-k[i]) + dis[d[i-1]][d[i]] * k[i-1] * k[i]; dp[i][j][1] = min(dp[i][j][1],min(dp[i-1][j-1][0] + dis[c[i-1]][d[i]] * k[i] + dis[c[i-1]][c[i]] * (1-k[i]),g)); } } rep(i,0,m) ans = min(ans,min(dp[n][i][0],dp[n][i][1])); printf("%.2lf\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。