Luogu 1580 [NOIP2016] 换教室
先用Floyed做亮点之间的最短路,设计dp,记dp[i][j][0]为到第i节课,换了j次课,当前有没有换课达到的期望耗费体力最小值
方程(太长了还是看代码吧):
dp[i][j][0]<-dp[i - 1][j][0]
dp[i][j][0]<-dp[i - 1][j][1]
dp[i][j][1]<-dp[i - 1][j - 1][0]
dp[i][j][1]<-dp[i - 1][j - 1][1]
~~感觉跟没说一样~~
初值: inf, dp[1][0][0] = dp[1][1][1] = 0
在每一次枚举m转移之前有 dp[i][0][0] = dp[i - 1][0][0] + dis[c[i - 1]][c[i]]
转移顺序:……显然从小到大枚举转移
【锅】
一开始没有想清楚状态之间的关系, 只设计了两维dp,于是,这个样例完美地救了我,下次在搞之前还是要手算一遍a
Code:
#include <cstdio> using namespace std; typedef double db; const int N = 2005; const int P = 305; const int inf = 1 << 29; int n, m, pNum, eNum, c[N], d[N], dis[P][P]; db k[N], dp[N][N][2]; template <typename T> inline T min(T x, T y) { return x > y ? y : x; } inline void floyed() { for(int l = 1; l <= pNum; l++) for(int i = 1; i <= pNum; i++) for(int j = 1; j <= pNum; j++) dis[i][j] = min(dis[i][j], dis[i][l] + dis[l][j]); } int main() { scanf("%d%d%d%d", &n, &m, &pNum, &eNum); 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 <= pNum; i++) for(int j = 1; j <= pNum; j++) if(i != j) dis[i][j] = inf; for(int x, y, v, i = 1; i <= eNum; i++) { scanf("%d%d%d", &x, &y, &v); dis[x][y] = min(dis[x][y], v); dis[y][x] = dis[x][y]; } floyed(); /* for(int i = 2; i <= n + 1; i++) for(int j = 1; j <= m; j++) dp[i][j] = (db)inf; for(int i = 1; i <= n + 1; i++) dp[i][0] = dp[i - 1][0] + (db) dis[c[i - 1]][c[i]]; db ans = dp[n + 1][0]; printf("%.2f ", dp[n + 1][0]); for(int i = 2; i <= n + 1; i++) for(int j = 1; j <= m; j++) { dp[i][j] = min(dp[i][j], dp[i - 1][j] + (db)dis[c[i - 1]][c[i]]); db t = dp[i - 2][j - 1]; t += (db) k[i] * (dis[c[i - 2]][d[i - 1]] + dis[d[i - 1]][c[i]]); t += (db) (1 - k[i]) * (dis[c[i - 2]][c[i - 1]] + dis[c[i - 1]][c[i]]); dp[i][j] = min(dp[i][j], t); if(i == n + 1) ans = min(dp[i][j], ans); } printf("%.2f %.2f %.2f ", dp[1][1], dp[2][1], dp[3][2]); ans = min(ans, dp[n + 1][0]); */ for(int i = 0; i<= n; i++) for(int j = 0; j <= m; j++) for(int l = 0; l <= 1; l++) dp[i][j][l] = (db) inf; dp[1][0][0] = dp[1][1][1] = 0; db ans = (db) inf; for(int i = 2; i <= n; i++) { dp[i][0][0] = dp[i - 1][0][0] + dis[c[i - 1]][c[i]]; for(int j = 1; j <= m; j++) { db p[5]; p[1] = k[i - 1], p[2] = k[i], p[3] = 1 - k[i - 1], p[4] = 1 - k[i]; int t[5]; t[1] = c[i - 1], t[2] = d[i - 1], t[3] = c[i], t[4] = d[i]; dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0] + dis[t[1]][t[3]]); dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][1] + p[3] * dis[t[1]][t[3]] + p[1] * dis[t[2]][t[3]]); dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0] + p[4] * dis[t[1]][t[3]] + p[2] * dis[t[1]][t[4]]); dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][1] + p[3] * p[4] * dis[t[1]][t[3]] + p[2] * p[3] * dis[t[1]][t[4]] + p[1] * p[4] * dis[t[2]][t[3]] + p[1] * p[2] * dis[t[2]][t[4]]); if(i == n) ans = min(ans, min(dp[i][j][0], dp[i][j][1])); } } ans = min(ans, dp[n][0][0]); printf("%.2f", ans); return 0; }