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;        
}

 

posted @ 2018-08-13 09:32  CzxingcHen  阅读(124)  评论(0编辑  收藏  举报