NOIP 2016 D1T3 换教室

为什么我觉得这年D1非常之丧……

题目链接

看到题目有点晕……,反正发现v<=500之后先写了floyd,不写白不写。

现在我们有任意两点之间的最短路了,考虑dp(i表示考虑到第i个时间段)

f[i][j][0]=min(跑到c[i]的期望距离)

f[i][j][1]表示min(跑到c[i]的期望距离*(1-k[i])+跑到d[i]的期望距离*k[i] )(即假设在i交了申请跑到第i个教室的最短期望距离)

什么意思呢?

先设 g[x] = 跑到x的期望距离

我们假设有 i 要从 i-1 转移。

假若i-1处不交转课申请,无论对于d[i]还是c[i] 我们肯定希望 g[c[i-1]] 越小越好。

假若i-1处交了转课申请,同理

我们肯定希望 (g[c[i-1]]+dis[c[i-1]][c[i]])*(1-k[i-1])+(g[d[i-1]]+dis[d[i-1]][c[i]])*k[i-1] 以及

       (g[c[i-1]]+dis[c[i-1]][d[i]])*(1-k[i-1])+(g[d[i-1]]+dis[d[i-1]][d[i]])*k[i-1]越小越好。

发现dis其实无法改变,考虑把dis删去,得到g[c[i-1]]*(1-k[i-1])+g[d[i-1]]*k[i-1],我们要最小化的是它。

这时我们发现一个dp[i][j]其实需要维护两个状态才能满足最优子结构的性质。

因此就有了上面的状态表示,我们考虑n^2转移即可,转移方程不难想,但是很难写……

代码(式子巨长,细节巨多,心力交瘁):

#include<cstdio>
#include<cstring>
#define min(a,b) (a<b?a:b)
#define init(x,p) memset(x,p,sizeof(x))
int map[505][505];
int n,m,v,e,c[2005],d[2005];
double k[2005],dp[2005][2005][4];
void init_dp();
void floyd();
int main(){
//    freopen("xx.txt","r",stdin);
    init(map,0x3f);
    scanf("%d%d%d%d",&n,&m,&v,&e);
    for(int i=1;i<=n;++i){
        scanf("%d",&c[i]);
        if(c[i]==0) return -1;
    }
    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<=e;++i){
        int a,b,cd;
        scanf("%d%d%d",&a,&b,&cd);
        map[a][b]=map[b][a]=min(map[b][a],cd);
    }
    for(int i=1;i<=v;++i) map[i][i]=0;
    floyd();
    init_dp();
    dp[1][0][0]=dp[1][0][1]=0;
    #define f map
    for(int i=2;i<=n;++i){
        for(int j=0;j<=min(n,m);++j){
            if(!j){
                dp[i][j][0]=dp[i-1][j][0]+f[c[i-1]][c[i]];
                dp[i][j][1]=f[c[i-1]][c[i]]*(1-k[i])+f[c[i-1]][d[i]]*k[i]+dp[i-1][j][0];
                continue;
            }
            dp[i][j][0]=min(
                dp[i-1][j][0]+f[c[i-1]][c[i]],
                dp[i-1][j-1][1]+f[d[i-1]][c[i]]*(k[i-1])+f[c[i-1]][c[i]]*(1-k[i-1])
            );
            dp[i][j][1]=min(
                f[c[i-1]][c[i]]*(1-k[i])+f[c[i-1]][d[i]]*k[i]+dp[i-1][j][0],
                (f[d[i-1]][c[i]]*(k[i-1])+f[c[i-1]][c[i]]*(1-k[i-1]))*(1-k[i])+(f[d[i-1]][d[i]]*(k[i-1])+f[c[i-1]][d[i]]*(1-k[i-1]))*k[i]+dp[i-1][j-1][1]
            );
        }
    }
    #undef f
//    printf("%.3f",min(f[n][j][]))
    double ans=1e10;
    for(int j=0;j<=m;++j){
        ans=min(ans,dp[n][j][0]);
        if(j!=m) ans=min(ans,dp[n][j][1]);
    }
    printf("%.2lf\n",ans);
    return 0;
}

void floyd(){
    for(int k=1;k<=v;++k)
        for(int i=1;i<=v;++i)
            for(int j=1;j<=v;++j)
                map[i][j]=min(map[i][k]+map[k][j],map[i][j]);
    return ;
}

void init_dp(){
    for(int i=0;i<2005;++i)
        for(int j=0;j<2005;++j)
            dp[i][j][0]=dp[i][j][1]=1e8;
}
丑代码

 

posted @ 2018-11-01 23:36  臼邦庶民  阅读(94)  评论(0编辑  收藏  举报