其实吧我老早就把这题切了……因为说实话,这道题确实不难啊……李云龙:比他娘的状压DP简单多了

今天我翻以前在Luogu上写的题解时,突然发现放错代码了,然后被一堆人\(hack\)……蓝瘦啊\(ORZ\)

嗯,还是有些点需要注意以下的!以下是今年4月写的:


\(\mathcal{\color{red}{Description}}\)

对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。

在可以选择的课程中,有 \(2n\)节课程安排在 \(n\)个时间段上。在第$ i $(\(1 \leq i \leq n\))个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室\(c_i\)上课,而另一节课程在教室$ d_i$ 进行。

在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 \(n\) 节安排好的课程。如果学生想更换第\(i\) 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 i 个时间段去教室$ d_i$上课,否则仍然在教室 \(c_i\)上课。

由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 \(i\) 节课程的教室时,申请被通过的概率是一个已知的实数 \(k_i\),并且对于不同课程的申请,被通过的概率是互相独立的。

学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 \(m\)节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的\(m\) 门课程,也可以不用完这 $m $个申请的机会,甚至可以一门课程都不申请。

因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。

牛牛所在的大学有\(v\)个教室,有\(e\)条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。 当第 \(i\) ( \(1 \leq i \leq n-1\))节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。

现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。

\(\mathcal{\color{red}{Solution}}\)

那么对于这道题而言,先捋清楚题目是求什么的吧:

对于这个无向连通图,我们将每走一步定义为一个阶段。那么每一个阶段都有两种可能性:\(p_i\)的概率去\(d_i\),但是在所有的\(d[i]\) 中\((1<=i<=n)\)至多可以走\(m\)个,\((1-p_i)\)的概率去\(c[i]\)。而我们要求的,就是在这\(n\)个阶段结束之后的路程最小期望。

那么其实状态之间的转移,我们不难看出有两种状态的转移:从\(d[i-1]\)或从\(c[i-1]\)转移过来。而因为实际上对于这个\(DP\)而言,因为数据不大,所以不需要优化什么的\(qwq\),记录每种状态是可行的。

那么很显然啊,我们首先要预处理出每两个点之间的最短路来,方便状态的转移。而在这里,最简单的就是\(Floyd\)啊\(qwq\)。\((n^3\)显然可以接受\()\)

    for(qwq int k=1;k<=v;k++)
for(qwq int i=1;i<=v;i++)
for(qwq int j=1;j<i;j++)
if(f[i][k]+f[k][j]<f[i][j])
f[i][j]=f[j][i]=f[i][k]+f[k][j];

然后就是\(DP\)方程了:

我们定义\(dp[i][j][0/1]\)来表示当前为第\(i\)个阶段,连同这一次已经用了\(j\)次换教室的机会,当前这次换\((1)\)不换\((0)\)的最小期望路程总和。

那么转移就可以如此转移:

这次不换:

\(dp[i][j][0]=\) \(min(\)上次不换的\(dp+\)这两次之间的路程 \(~~\), \(~~\)上次概率换了之后的\(dp+p[i]\times\)上次换了的教室与这次不换的教室之间的距离\(+(1-p[i])\times\)上次不换的教室与这次不换的教室之间的距离\()\)

“诶,为什么上次概率换了之后(即逗号之后的一大串)要加两个期望啊?”

这个问题就是\(rqy\)大佬给我解决的,现在我要农夫山泉一把了:因为在上一次换教室时是“概率”交换,所以不一定会换呀。所以要把两种情况的都加上\(qwq\)。

到这儿我们就可以发现,其实换教室比不换教室是要多一重状态的,因为换教室总要牵扯“概率成功”的问题\(qwq\)

那其实接下来的状态转移方程就很简单了:一次换一次不换,遇到不换就\((1-p[i])\),遇到换就\(p[i]\);两次都不换就不用枚举概率。而且由于牵扯到两次都是概率性事件(比如两次都换)之类的,这个时候需要的就是乘法原理了。

那么\(DP\)即如下:

#define qwq register 

    for(qwq int i=1;i<=n;i++)
for(qwq int j=0;j<=m;j++)
dp[i][j][0]=dp[i][j][1]=999999999; dp[1][0][0]=dp[1][1][1]=0;
for(qwq int i=2;i<=n;i++){
double dist1=f[c[i-1]][c[i]],dist2=f[d[i-1]][c[i]],dist3=f[c[i-1]][d[i]];
for(qwq int j=0;j<=min(m,i);j++)
{
dp[i][j][0]=min(dp[i-1][j][0]+dist1,dp[i-1][j][1]+dist2*p[i-1]+dist1*(1-p[i-1]));
if(j!=0)
dp[i][j][1]=min(dp[i-1][j-1][0]+dist3*p[i]+dist1*(1-p[i]),dp[i-1][j-1][1]+dist1*(1-p[i-1])*(1-p[i])+dist3*(1-p[i-1])*p[i]+dist2*(1-p[i])*p[i-1]+dist2*p[i-1]*p[i]);
}
}

总结:遇到期望的题目时一定要全面考虑啊!我们可以发现这个题的\(dp\)方程其实并不难想。

完结撒花!

//感谢rqy大佬qwqqq
#include<iostream>
#include<cstdio>
#define qwq register
using namespace std;
double p[10001],f[2001][2001],dp[2001][2001][2];
int a[2001][2001],c[20001],d[20001];
inline double min(double a,double b){
return a<b?a:b;
}
inline int qread(){
int k = 0;
char c;
c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c)){
k = (k<<1)+(k<<3)+c-48;
c = getchar();
}
return k ;
}
inline double qread_double()
{
double k=0;char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))k=k*10+(c-48),c=getchar();
if(c=='.')
{
double base=0.1;c=getchar();
while(isdigit(c))k=k+(c-48)*base,base/10,c=getchar();
}
return k;
}
int main()
{
int n,m,v,e,a1,b1,c1;
cin>>n>>m>>v>>e;
for(qwq int i=1;i<=n;i++)c[i]=qread();
for(qwq int i=1;i<=n;i++)d[i]=qread();
for(qwq int i=1;i<=n;i++)p[i]=qread_double(); for(qwq int i=1;i<=v;i++)
for(qwq int j=1;j<i;j++)
f[i][j]=f[j][i]=999999999; for(qwq int i=1;i<=e;i++){
a1=qread(),b1=qread(),c1=qread();
f[a1][b1]=f[b1][a1]=min(f[a1][b1],c1);
} for(qwq int k=1;k<=v;k++)
for(qwq int i=1;i<=v;i++)
for(qwq int j=1;j<i;j++)
if(f[i][k]+f[k][j]<f[i][j])
f[i][j]=f[j][i]=f[i][k]+f[k][j]; for(qwq int i=1;i<=n;i++)
for(qwq int j=0;j<=m;j++)
dp[i][j][0]=dp[i][j][1]=999999999; dp[1][0][0]=dp[1][1][1]=0;
for(qwq int i=2;i<=n;i++){
double add1=f[c[i-1]][c[i]];
for(qwq int j=0;j<=min(m,i);j++)
{
dp[i][j][0]=min(dp[i-1][j][0]+add1,dp[i-1][j][1]+f[d[i-1]][c[i]]*p[i-1]+f[c[i-1]][c[i]]*(1-p[i-1]));
if(j!=0)
dp[i][j][1]=min(dp[i-1][j-1][0]+f[c[i-1]][d[i]]*p[i]+f[c[i-1]][c[i]]*(1-p[i]),dp[i-1][j-1][1]+f[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+f[c[i-1]][d[i]]*(1-p[i-1])*p[i]+f[d[i-1]][c[i]]*(1-p[i])*p[i-1]+f[d[i-1]][d[i]]*p[i-1]*p[i]);
}
} double hahaha=9999999999;
for(int i=0;i<=m;i++){
hahaha=min(dp[n][i][0],min(dp[n][i][1],hahaha));}
printf("%.2lf",hahaha);
}

\(By\) \(Flower\) _ \(pks\)

[NOIP2016]换教室(概率期望$DP$)的更多相关文章

  1. [NOIP2016]换教室 D1 T3 Floyed+期望DP

    [NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 ...

  2. bzoj4720: [Noip2016]换教室(期望dp)

    4720: [Noip2016]换教室 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1294  Solved: 698[Submit][Status ...

  3. BZOJ 4720 [Noip2016]换教室

    4720: [Noip2016]换教室 Description 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程.在可以选择的课程中,有2n节课程安排在n个时间段上.在第i( ...

  4. 【BZOJ】4720: [Noip2016]换教室

    4720: [Noip2016]换教室 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1690  Solved: 979[Submit][Status ...

  5. [NOIP2016]换教室 题解(奇怪的三种状态)

    2558. [NOIP2016]换教室 [题目描述] 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第i(1< ...

  6. 【bzoj4720】[NOIP2016]换教室 期望dp

    题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程.在可以选择的课程中,有2n节课程安排在n个时间段上.在第i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的 ...

  7. 「NOIP2016」「P1850」 换教室(期望dp

    题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 2n2n 节课程安排在 nn 个时间段上.在第 ii(1 \leq i \leq n1≤ ...

  8. 【bzoj4720】[NOIP2016]换教室

    题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程.在可以选择的课程中,有2n节课程安排在n个时间段上.在第i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的 ...

  9. [NOIp2016] 换教室

    题目类型:期望\(DP\) 传送门:>Here< 题意:现有\(N\)个时间段,每个时间段上一节课.如果不申请换教室,那么时间段\(i\)必须去教室\(c[i]\)上课,如果申请换课成功, ...

随机推荐

  1. Uncaught TypeError: timeout.close is not a function. when try to use clearInterval

    It's because of your IDE! Make sure you have added automatic imports such as import { clearInterval ...

  2. vue2.0路由写法

    // 0. 如果使用模块化机制编程,導入Vue和VueRouter,要调用 Vue.use(VueRouter) // 1. 定义(路由)组件. // 可以从其他文件 import 进来 var Fo ...

  3. Linux Notes

    Do what we want based on what others already did with additional abstraction and organization to ser ...

  4. xshell连接虚拟机Connection failed

    一.问题描述:xshell连接不了虚拟机,出现错误提示:Could not connect to '192.168.1.100' (port 22): Connection failed. 二.查找错 ...

  5. Android 线程池的类型、区别以及为何要用线程池

    每个 Android 应用进程在创建时,会同时创建一个线程,我们称之为主线程,负责更新 UI 界面以及和处理用户之间的交互,因此,在 Android 中,我们又称之为 UI 线程.一个进程中 UI 线 ...

  6. 检查 NaN 数据值 (C/C++/Python 实现)

    NaN 是 Not a Number 的缩写.它是一个数值类型值,通常在浮点计算中,表示未定义或无法表示的值.而且,不能直接使用相等运算符 (==) 检查 NaN.由于在程序中,nan == nan ...

  7. 为什么使用.Net Core, Asp.net Core以及部署到云端

    一,基于微软技术平台开发的应用项目,如果有以下的需求和潜在需求,建议使用.Net Core和Asp.net core技术架构: 1. 跨平台支持 2. 采用微服务架构 3. 使用Docker容器 4. ...

  8. mysql错误:Statement violates GTID consistency

    在MYSQL中,执行建表语句时CREATE TABLE  aaaa  AS SELECT * FROM menu;  报: 错误代码: 1786Statement violates GTID cons ...

  9. 分享:linux系统如何快速阻止恶意IP地址

    可能你想要在各种情形下阻止有人通过IP地址访问你的Linux系统.比如说,作为最终用户,你可能想要保护自己,避免已知的间谍软件或跟踪者的IP地址.或者如果你在运行P2P软件,可能想要把来自与违反P2P ...

  10. js中问号

    是三目运算,如:(a==b)?a:b 也就是说,先判断a是否等于b,如果是(true),那么返回a,如果否(false),则返回b greeting=(visitor=="PRES" ...