题目传送门

首先这个题我们一看它就是和概率期望有关,而大多数时候在OI中遇到他们时,都是与dp相关的。

\(Vergil\)学长表示,作为\(NOIp2016\)的当事人,他们考前奶联赛一定不会考概率期望,结果...真香!\(qwq\)。

不过\(NOIp\)还是对像我这样的菜到不会正解只会写暴力的蒟蒻来说还是很友好的==。据说这题暴力分都拿满有\(80pts+\)。作为第三题的分量真的很友好。

暑假学长就是用的这个题给我们讲的二进制枚举。性感学长在线\(debug\)。

64分做法:

注意到1~15测试点的\(n\)范围在\(20\)内,考虑二进制枚举。具体来说,就是把\(n\)个时间段压在一个二进制位上,如果是0,说明他没选择换,如果是1,说明他换了(用了申请机会)。但是注意,因为申请成功是有概率的,即你换了也不一定成功,所以我们还要枚举申请的订单是否能成功。

也就是说,这是一个二进制枚举套二进制枚举。第一步,枚举申请换的教室;第二步,枚举申请的教室是否成功。

至于两点间的距离,注意到\(v<=300\),我们可以用\(floyd\)算法在\(O(v^3)\)的复杂度内完成最短路计算。

  1. memset(dis,0x3f,sizeof(dis));
  2. for(int i=1;i<=e;i++)
  3. {
  4. int x=0,y=0,z=0;
  5. scanf("%d%d%d",&x,&y,&z);
  6. dis[x][y]=min(dis[x][y],z);
  7. dis[y][x]=min(dis[y][x],z);
  8. }
  9. for(int i=1;i<=v;i++) dis[0][i]=0,dis[i][i]=0;
  10. for(int k=1;k<=v;k++)
  11. for(int i=1;i<=v;i++)
  12. for(int j=1;j<=v;j++)
  13. dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);

至于二进制枚举,我们可以用一个简单的位运算技巧来简化代码。

  1. for(int j=0;j<n;j++)
  2. if((1<<j)&i) cnt++;

其中\(i\)是我们枚举的状态(压成十进制后的),满足\(if\)中的条件即证明\(j\)这位在\(i\)表示的二进制数下为1.

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. int n,m,v,e,fake,tot,pos;
  6. int c[500],d[500],sta[500],fk[500],dis[500][500];
  7. double ans=1e9,kk[500];
  8. int main()
  9. {
  10. scanf("%d%d%d%d",&n,&m,&v,&e);
  11. for(int i=1;i<=n;i++) scanf("%d",&c[i]);
  12. for(int i=1;i<=n;i++) scanf("%d",&d[i]);
  13. for(int i=1;i<=n;i++) scanf("%lf",&kk[i]);
  14. memset(dis,0x3f,sizeof(dis));
  15. for(int i=1;i<=e;i++)
  16. {
  17. int x=0,y=0,z=0;
  18. scanf("%d%d%d",&x,&y,&z);
  19. dis[x][y]=min(dis[x][y],z);
  20. dis[y][x]=min(dis[y][x],z);
  21. }
  22. for(int i=1;i<=v;i++) dis[0][i]=0,dis[i][i]=0;
  23. for(int k=1;k<=v;k++)
  24. for(int i=1;i<=v;i++)
  25. for(int j=1;j<=v;j++)
  26. dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]);
  27. fake=(1<<n)-1;
  28. for(int i=0;i<=fake;i++)
  29. {
  30. int cnt=0;tot=0;pos=0;
  31. double re=0;
  32. for(int j=0;j<n;j++)
  33. if((1<<j)&i) cnt++;
  34. //第一步 枚举申请换的教室
  35. if(cnt>m) continue;
  36. for(int j=0;j<n;j++)
  37. if((1<<j)&i) sta[++tot]=j+1;
  38. //记录都是哪些教室申请换了
  39. int res=(1<<tot)-1;
  40. //第二步,枚举申请的教室是否成功
  41. for(int k=0;k<=res;k++)
  42. {
  43. for(int o=1;o<=n;o++) fk[o]=0;
  44. //清空标记
  45. for(int o=0;o<tot;o++)
  46. if((1<<o)&k) fk[sta[o+1]]=1;
  47. //记录当前枚举的申请情况
  48. int be=0,dist=0;
  49. double p=1;
  50. for(int qwq=1;qwq<=n;qwq++)
  51. {
  52. if(i&(1<<(qwq-1)))
  53. {//申请了 但是可能成功或没成功
  54. int to=fk[qwq] ? d[qwq] : c[qwq];
  55. dist+=dis[be][to];
  56. be=to;
  57. double hu=fk[qwq] ? kk[qwq] : 1-kk[qwq];
  58. p*=hu;
  59. }
  60. else dist+=dis[be][c[qwq]],be=c[qwq];
  61. }
  62. re+=dist*p;
  63. }
  64. ans=min(ans,re);
  65. }
  66. printf("%.2lf",ans);
  67. return 0;
  68. }

堪称二进制枚举的经典鸭

满分做法:

当然是\(dp\)辣hhh。

状态和转移感觉设计并不难。我们考虑设计这样一个状态:\(f[i][j][0]\)和\(f[i][j][1]\)。其中\(f[i][j][0]\)表示当前是第\(i\)节课,之前包括现在共已申请了\(j\)个订单,当前没有申请订单。而\(f[i][j][1]\)即为当前申请了订单。

显然我们当前的状态是从之前的状态转移而来的,当前有换和不换两种选择(状态设计中),那么上一个状态即\(i-1\)也有换与不换两种选择。我们只要分别捋清楚就行了。只是需要注意的是:首先期望是相加的,因为概率的基本性质,互斥的事件可加。(我也布吉岛这么说是否准确\(qwq\))其次,只要存在申请订单的时刻,那么必然有申请成功和申请失败两种事件,因为申请订单是一个随机事件,成功不是必然事件,也不是不可能事件,于是我们需要分别算他们的期望,相加。

于是我们得到了十分冗杂的转移方程(虽然麻烦,但是明白上一点后非常清楚好想)

\(f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1]+k[i-1]*dis[c[i]][d[i-1]]+(1-k[i-1])*dis[c[i]][c[i-1]])\)

\(f[i][j][1]=min(f[i-1][j-1][0]+k[i]*dis[d[i]][c[i-1]]+(1-k[i])*dis[c[i]][c[i-1]],f[i-1][j-1][1]+k[i]*k[i-1]*dis[d[i]][d[i-1]]+k[i]*(1-k[i-1])*dis[d[i]][c[i-1]]\)

最后我们找出最小的合法答案。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. int n,m,v,e;
  6. int c[3000],d[3000],dis[500][500];
  7. double ans=1e9,k[3000],f[2500][2500][3];
  8. int main()
  9. {
  10. scanf("%d%d%d%d",&n,&m,&v,&e);
  11. for(int i=1;i<=n;i++) scanf("%d",&c[i]);
  12. for(int i=1;i<=n;i++) scanf("%d",&d[i]);
  13. for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
  14. memset(dis,0x3f,sizeof(dis));
  15. for(int i=1;i<=v;i++) dis[i][i]=0,dis[0][i]=0;
  16. for(int i=1;i<=e;i++)
  17. {
  18. int x=0,y=0,z=0;
  19. scanf("%d%d%d",&x,&y,&z);
  20. dis[x][y]=min(dis[x][y],z);
  21. dis[y][x]=min(dis[y][x],z);
  22. }
  23. for(int kk=1;kk<=v;kk++)
  24. for(int i=1;i<=v;i++)
  25. for(int j=1;j<=v;j++)
  26. dis[i][j]=min(dis[i][j],dis[i][kk]+dis[kk][j]);
  27. for(int i=1;i<=n;i++)
  28. for(int j=0;j<=m;j++)
  29. f[i][j][0]=f[i][j][1]=1e9;
  30. f[1][0][0]=0;
  31. f[1][1][1]=0;
  32. for(int i=2;i<=n;i++)
  33. for(int j=0;j<=m;j++)
  34. {
  35. f[i][j][0]=min(f[i-1][j][0]+dis[c[i]][c[i-1]],f[i-1][j][1]+k[i-1]*dis[c[i]][d[i-1]]+(1-k[i-1])*dis[c[i]][c[i-1]]);
  36. if(j==0) continue;
  37. f[i][j][1]=min(f[i-1][j-1][0]+k[i]*dis[d[i]][c[i-1]]+(1-k[i])*dis[c[i]][c[i-1]],f[i-1][j-1][1]+k[i]*k[i-1]*dis[d[i]][d[i-1]]+k[i]*(1-k[i-1])*dis[d[i]][c[i-1]]+(1-k[i])*k[i-1]*dis[c[i]][d[i-1]]+(1-k[i])*(1-k[i-1])*dis[c[i]][c[i-1]]);
  38. }
  39. for(int i=0;i<=m;i++)
  40. for(int j=0;j<=1;j++)
  41. ans=min(ans,f[n][i][j]);
  42. printf("%.2lf",ans);
  43. return 0;
  44. }

另一些细节:

本题变量名极其容易搞混,因为太多了orz。\(Vergil\)学长就是这样在考场上\(68pts->8pts\)。平时的点边习惯用\(n\)而这里是\(v\)。

\(f\)数组的赋初值:\(f[1][0][0]=0\),\(f[1][1][1]=0\)。

转移的时候注意边界。\(i\)从2开始,第二种转移在\(j=0\)时不能进行转移

题出的好!难度适中,覆盖知识点广,题目又着切合实际的背景,解法比较自然。给出题人点赞 !

Luogu P1850换教室【期望dp】By cellur925的更多相关文章

  1. Luogu P1850 换教室(期望dp)

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

  2. P1850 换教室 期望dp

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

  3. P1850 换教室——期望DP

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

  4. 换教室(期望+DP)

    换教室(期望+DP) \(dp(i,j,1/0)\)表示第\(i\)节课,申请了\(j\)次调换,这节课\(1/0\)调换. 换教室 转移的时候考虑: 上次没申请 这次也没申请 加上\(dis(fr[ ...

  5. Bzoj 4720 换教室 (期望DP)

    刚发现Bzoj有Noip的题目,只会换教室这道题..... Bzoj 题面:Bzoj 4720 Luogu题目:P1850 换教室 大概是期望DPNoip极其友好的一道题目,DP不怎么会的我想到了,大 ...

  6. Luogu P1850 [NOIp2016提高组]换教室 | 期望dp

    题目链接 思路: <1>概率与期望期望=情况①的值*情况①的概率+情况②的值*情况②的概率+--+情况n的值*情况n的概率举个例子,抛一个骰子,每一面朝上的概率都是1/6,则这一个骰子落地 ...

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

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

  8. 【BZOJ4720】【NOIP2016】换教室 [期望DP]

    换教室 Time Limit: 20 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description Input 第一行四个整数n,m,v ...

  9. 【bzoj4720】[Noip2016]换教室 期望dp+最短路

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

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

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

随机推荐

  1. 通达OA 一些工作流调整后带来的后果及应对措施

    近期单位有个工作流须要改动,原因是最早设计时控件的字段设计不规范,控件直接使用了人员的名字来命名了.这不使用手机訪问时就出问题了,名字会直接显示出来,如今就须要进行调整. 调整初步有两个方案: 一是全 ...

  2. java解析xml的方式DOM,SAX,DOM4J,JDOM,StAX

    1)DOM(JAXP Crimson解析器)DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找 特定信息. ...

  3. iOS 内购遇到的坑

    一.内购沙盒测试账号在支付成功后,再次购买相同 ID 的物品,会提示如下内容的弹窗.您以购买过此APP内购项目,此项目将免费恢复 原因: 当使用内购购买过商品后没有把这个交易事件关,所以当我们再次去购 ...

  4. VUE 之 路由 VueRouter

    1.VueRouter的安装 1.1.https://unpkg.com/vue-router/dist/vue-router.js下载安装. 1.2.<script src="./s ...

  5. Comparing Random and Sequential Access in Disk and Memory

    The Pathologies of Big Data - ACM Queue https://queue.acm.org/detail.cfm?id=1563874

  6. update外联表,用另一个表数据更新本表数据

    update s set s.classbid = lc.itemidfrom    dbo.Lv_servers as s INNER JOIN dbo.Lv_LineChannel as lc O ...

  7. Linux Linker

    文章原文:http://zhidao.baidu.com/link?url=U2Mtcc6BKi4vuQ1MO8U6s9gNm4y9Epphz03veA2lVpRWMozyVdj0PYvw1ZU9qj ...

  8. python selenium实现百度搜索

    1.环境 python2.7+selenium+phantomjs+linux 2.代码 #-*-coding:utf-8 -*- from selenium import webdriver fro ...

  9. SDUT 3033 这题实在不知道起啥名好了(思维巧法)

    这题实在不知道起啥名好了 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 懒得想背景故事了,开门见山. 有一个长度为n的整数数列A ...

  10. HDU3085 Nightmare Ⅱ —— 双向BFS + 曼哈顿距离

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3085 Nightmare Ⅱ Time Limit: 2000/1000 MS (Java/Other ...