• [NOIp2016] 换教室


    题目类型:期望(DP)

    传送门:>Here<

    题意:现有(N)个时间段,每个时间段上一节课。如果不申请换教室,那么时间段(i)必须去教室(c[i])上课,如果申请换课成功,那么就可以去教室(d[i])上课。第(i)节课申请换教室成功的概率是(k[i])。每个教室是无向图的一个节点,从一个教室到另一个教室需要耗费的体力是它们之间的最短路。现在,你最多可以申请换(M)节课,问耗费体力值最少的期望

    解题思路

    题意比较复杂。时间段不如理解为时间点。可以概括为:第(i)个时间点要么在(c[i])要么在(d[i]),并且到(d[i])去的期望是(k[i])。也就是说申请不成功的概率是(1-k[i])

    由于教室最多只有(300)间,因此最短路直接用(Floyd)处理即可。

    然后考虑进行期望(DP)。容易想到设(dp[i][j])表示前(i)个时间点里,申请(j)次的耗费体力值最少的期望。然而我们发现这样设非常难转移,因为我们不知道上一节课有没有换教室。

    因此我们增加一维:(dp[i][j][k])表示前(i)个时间点里,申请(j)次,并且(k=0)(i)个时间点在(c[i])(k=1)则在(d[i])。这样就可以转移了

    由于已经转化为(DP)问题,因此我们只需要考虑状态。分开考虑:

    [dp[i][j][0] = egin{cases} dp[i-1][j][0] + dis[c[i-1]][c[i]]\ dp[i-1][j][1] + (1-k[i-1])*dis[c[i-1]][c[i]] + k[i-1]*dis[d[i-1]][c[i]] end{cases} ]

    对于(dp[i][j][0])的转移,我们确定了在第(i)个时间点一定在教室(c[i]),而起点却不确定。分开考虑乘上概率即可

    [dp[i][j][1] = egin{cases} dp[i-1][j-1][0] + (1-k[i])*dis[c[i-1]][c[i]] + k[i]*dis[c[i-1]][d[i]]\ dp[i-1][j-1][1] +... end{cases} ]

    第二个方程实在太长了(放不下……),可以见代码。总体思想还是和前面差不多,不一样的是(dp[i][j][1])不能代表第(i)个时间点在教室(d[i]),而是都有可能,因此从(dp[i-1][j-1][1])转移过来时要分四类讨论

    Code

    注意(j=0)也是要讨论的。另外,刚开始(dp)数组应该无限大,这样才能在转移时自动排除不可能的情况

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    using namespace std;
    typedef long long ll;
    const int MAXN = 2010;
    const int MAXM = 20010;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    int N,M,V,E,x,y,z;
    int c[MAXN],d[MAXN],dis[305][305];
    double k[MAXN],dp[MAXN][MAXN][2],ans;
    int main(){
    //	freopen(".in","r",stdin);
    	memset(dis, 0x3f, sizeof(dis));
    	N = r, M = r, V = r, E = r;
    	for(int i = 1; i <= V; ++i) dis[i][i] = 0;
    	for(int i = 1; i <= N; ++i) c[i] = r;
    	for(int i = 1; i <= N; ++i) d[i] = r;
    	for(int i = 1; i <= N; ++i) scanf("%lf", k+i);
    	for(int i = 1; i <= E; ++i){
    		x = r, y = r, z = r;
    		dis[x][y] = min(dis[x][y], z);
    		dis[y][x] = min(dis[y][x], z);
    	}
    	for(int K = 1; K <= V; ++K){
    		for(int i = 1; i <= V; ++i){
    			for(int j = 1; j <= V; ++j){
    				dis[i][j] = min(dis[i][j], dis[i][K] + dis[K][j]);
    			}
    		}
    	}
    	for(int i = 1; i <= N; ++i){
    		for(int j = 0; j <= M; ++j){
    			dp[i][j][0] = dp[i][j][1] = 99999999.999;
    		}
    	}
    	dp[1][0][0] = dp[1][1][1] = 0;
    	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 <= min(i,M); ++j){
    			dp[i][j][0] = min(dp[i-1][j][0] + dis[c[i-1]][c[i]], dp[i-1][j][1] + (1-k[i-1])*dis[c[i-1]][c[i]] + k[i-1]*dis[d[i-1]][c[i]]);
    			dp[i][j][1] = min(dp[i-1][j-1][0] + (1-k[i])*dis[c[i-1]][c[i]] + k[i]*dis[c[i-1]][d[i]], dp[i-1][j-1][1] + (1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]] + (1-k[i-1])*k[i]*dis[c[i-1]][d[i]] + k[i-1]*(1-k[i])*dis[d[i-1]][c[i]] + k[i-1]*k[i]*dis[d[i-1]][d[i]]);
    		}
    	}
    	ans = 9999999.999;
    	for(int j = 0; j <= M; ++j){
    		ans = min(ans, min(dp[N][j][0], dp[N][j][1]));
    	}
    	printf("%.2f", ans);
    	return 0;
    }
    
  • 相关阅读:
    Django_05_模板
    Django_04_视图
    Django_03_后台管理
    Django_02_创建模型
    Django_01_创建图书管理项目
    Djang简介
    day_03比特币转账的运行原理
    day_02比特币的转账机制及其7个名词
    day01_人类社会货币的演变
    Socket问题
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9649889.html
Copyright © 2020-2023  润新知