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

2558. [NOIP2016]换教室

【题目描述】

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

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

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

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

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

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

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

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

【输入格式】

从文件中读入数据。

第一行四个整数n, m, v, e . n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室;v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量。

第二行n个正整数,第i(1<=i<=n)个正整数表示ci,即第i个时间段牛牛被安排上课的教室;保证1<=ci<=v.

第三行n个正整数,第i(1<=i<=n)个正整数表示di,即第i个时间段另一间上同样课程的教室;保证1=<di<=v .

第四行n个实数,第i(1<=i<=n)个实数表示ki,即牛牛申请在第i个时间段更换教室获得通过的概率。保证0<=ki<=1。

接下来e行,每行三个正整数aj,bj,wj,表示有一条双向道路连接教室aj,bj,通过这条道路需要耗费的体力值是Wj;保证1<=aj,bj<=v,  1<=wj<=1000

保证1<=n<=2000,0<=m<=2000,1<=v<=300,0<=e<=90000。

保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。

保证输入的实数最多包含3位小数。

【输出格式】

输出到文件中。

输出一行,包含一个实数,四舍五入精确到小数点后恰好2位,表示答案。你的输出必须和标准输出完全一样才算正确。

测试数据保证四舍五入后的答案和准确答案的差的绝对值不大于4*10^-3(如果你不知道什么是浮点误差,这段话可以理解为:对于大多数的算法,你可以正常地使用浮点数类型而不用对它进行特殊的处理)

【样例1输入】

3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1

【样例1输出】

2.80

【提示】

所有可行的申请方案和期望收益如下表

【来源】

1.道路中可能会有多条双向道路连接相同的两间教室。也有可能有道路两端连接的是同一间教室。

2.请注意区分n, m, v, e的意义,n不是教室的数量,m不是道路的数量。

  这道题当时以为又是一个大坑,先放着没做。最近填坑时搞掉了,发现真容易。

  不知道为啥,很多人都写两种状态,就我写了三种状态,(其实本质一样)于是调一个小坑调了半天。

  首先基本是所有人都会去想的,状态为当前是第几节课,用了几次,现在在哪,然而这样并不可行。这道题本质是让我们去求在哪节课去申请调换,因此是否申请必须从状态之中表现出来,因此我们的状态为当前是第几节课,用了几次,这次是否申请,当时我为了方便容易自己理解,设定了三种状态,1.未申请 2.申请但失败 3.申请且成功(2,3中不体现本次是否成功的概率,仅在转移是体现)。于是这样我们的转移方程就出来了。

  

 1        f[i][0][0]=f[i-1][0][0]+dis[c[i-1]][c[i]];
 3        if(j<=i-2) f[i][j][0]=min(f[i-1][j][0]+dis[c1][cc],(f[i-1][j][1]+dis[c1][cc])*(1-k[i-1])+(f[i-1][j][2]+dis[d1][cc])*k[i-1]);
 4             else if(j<=i-1) f[i][j][0]=(f[i-1][j][1]+dis[c1][cc])*(1-k[i-1])+(f[i-1][j][2]+dis[d1][cc])*k[i-1];
 5             if(j==i)f[i][j][0]=0x7ffffff;
 6             f[i][j][1]=min(f[i-1][j-1][0]+dis[c1][cc],(f[i-1][j-1][1]+dis[c1][cc])*(1-k[i-1])+(f[i-1][j-1][2]+dis[d1][cc])*k[i-1]);
 7             f[i][j][2]=min(f[i-1][j-1][0]+dis[c1][dd],(f[i-1][j-1][1]+dis[c1][dd])*(1-k[i-1])+(f[i-1][j-1][2]+dis[d1][dd])*k[i-1]);
 8             if(j==1)
 9             {
10                 f[i][j][1]=f[i-1][j-1][0]+dis[c1][cc];
11                 f[i][j][2]=f[i-1][j-1][0]+dis[c1][dd];
12             }
  但是这样我们还有三个点是过不了的,因为我们在这里忽略了一个事情,1,2必须从同一位置转移,也就是说我们这样可能导致1,2一个是从上次申请转移过来,一个是从上次未申请转移过来,然后就WA成狗了。  所以我们要引进一个2.0版。 
 1        f[i][0][0]=f[i-1][0][0]+dis[c[i-1]][c[i]];
 2        if((f[i-1][j-1][0]+dis[c1][cc])*(1.0-k[i])+(f[i-1][j-1][0]+dis[c1][dd])*k[i]-0.000000001>((f[i-1][j-1][1]+dis[c1][dd])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][dd])*k[i-1])*k[i]+((f[i-1][j-1][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][cc])*k[i-1])*(1.0-k[i]))
 3             {
 4                 f[i][j][1]=(f[i-1][j-1][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][cc])*k[i-1];
 5                 f[i][j][2]=(f[i-1][j-1][1]+dis[c1][dd])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][dd])*k[i-1];
 6             }
 7             else
 8             {
 9                 f[i][j][1]=f[i-1][j-1][0]+dis[c1][cc];
10                 f[i][j][2]=f[i-1][j-1][0]+dis[c1][dd];

  这样就OK了。在此感谢Q某犇提供的标程帮助我调试。

 

  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<map>
  7 #include<queue>
  8 #include<string>
  9 #include<cmath>
 10 using namespace std;
 11 int n,m,v,e,zz;
 12 int c[2001],a[401],d[2001];
 13 double k[2001];
 14 struct ro{
 15     int to;
 16     double l;
 17     int next;
 18 }road[180005];
 19 void build(int x,int y,int z){
 20     zz++;
 21     road[zz].to=y;
 22     road[zz].next=a[x];
 23     road[zz].l=z;
 24     a[x]=zz;
 25 }
 26 bool rd[450];
 27 double dis[450][450];
 28 queue<int> q1;
 29 void spfa(int t){
 30     memset(rd,0,sizeof(rd));
 31     rd[t]=1;
 32     dis[t][t]=0.0;
 33     q1.push(t);
 34     while(!q1.empty())
 35     {
 36         int x=q1.front();
 37         q1.pop();
 38         rd[x]=0;
 39         for(int i=a[x];i>0;i=road[i].next)
 40         {
 41             int y=road[i].to;
 42             if(dis[t][y]>dis[t][x]+road[i].l)
 43             {
 44                 dis[t][y]=dis[t][x]+road[i].l;
 45                 if(!rd[y])
 46                 {
 47                     rd[y]=1;
 48                     q1.push(y);
 49                 }
 50             }
 51         }
 52     }
 53 }
 54 double f[2001][2005][3];
 55 int main(){
 56     freopen("classrooma.in","r",stdin);
 57     freopen("classrooma.out","w",stdout);
 58     scanf("%d%d%d%d",&n,&m,&v,&e);
 59     for(int i=0;i<=v;i++)
 60         for(int j=0;j<=v;j++)
 61             dis[i][j]=0x7fffffff;
 62     for(int i=1;i<=n;i++)
 63         scanf("%d",&c[i]);
 64     for(int i=1;i<=n;i++)
 65         scanf("%d",&d[i]);
 66     for(int i=1;i<=n;i++)
 67         scanf("%lf",&k[i]);
 68     for(int i=1;i<=e;i++)
 69     {
 70         int x,y,z;
 71         scanf("%d%d%d",&x,&y,&z);
 72         build(x,y,z);
 73         build(y,x,z);
 74     }
 75     for(int i=1;i<=v;i++)
 76     {
 77         dis[0][i]=dis[i][0]=0.0;
 78         spfa(i);
 79     }
 80     k[0]=1;
 81     f[0][0][0]=f[0][0][1]=0.0;
 82     for(int i=1;i<=n;i++)
 83     {
 84         f[i][0][0]=f[i-1][0][0]+dis[c[i-1]][c[i]];
 85         for(int j=1;j<=m;j++)
 86         {
 87             if(j>i)break;
 88             int cc=c[i],c1=c[i-1],dd=d[i],d1=d[i-1];
 89             if(j<=i-2) f[i][j][0]=min(f[i-1][j][0]+dis[c1][cc],(f[i-1][j][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j][2]+dis[d1][cc])*k[i-1]);
 90             else if(j<=i-1) f[i][j][0]=(f[i-1][j][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j][2]+dis[d1][cc])*k[i-1];
 91             if(j==i) f[i][j][0]=0x7ffffff;
 92             if((f[i-1][j-1][0]+dis[c1][cc])*(1.0-k[i])+(f[i-1][j-1][0]+dis[c1][dd])*k[i]-0.000000001>((f[i-1][j-1][1]+dis[c1][dd])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][dd])*k[i-1])*k[i]+((f[i-1][j-1][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][cc])*k[i-1])*(1.0-k[i]))
 93             {
 94                 f[i][j][1]=(f[i-1][j-1][1]+dis[c1][cc])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][cc])*k[i-1];
 95                 f[i][j][2]=(f[i-1][j-1][1]+dis[c1][dd])*(1.0-k[i-1])+(f[i-1][j-1][2]+dis[d1][dd])*k[i-1];
 96             }
 97             else
 98             {
 99                 f[i][j][1]=f[i-1][j-1][0]+dis[c1][cc];
100                 f[i][j][2]=f[i-1][j-1][0]+dis[c1][dd];
101             }
102             if(j==1)
103             {
104                 f[i][j][1]=f[i-1][j-1][0]+dis[c1][cc];
105                 f[i][j][2]=f[i-1][j-1][0]+dis[c1][dd];
106                 continue;
107             }
108         }
109     }
110     double ans=f[n][0][0];
111     for(int i=1;i<=m;i++)
112     {
113         ans=min(ans,min(f[n][i][0],f[n][i][1]*(1.0-k[n])+f[n][i][2]*k[n]));
114     }
115     printf("%.2lf\n",ans);
116     //while(1);
117     return 0;
118 }


 
时间: 2024-10-09 22:31:46

[NOIP2016]换教室 题解(奇怪的三种状态)的相关文章

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

[NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 i ( 1≤ i≤n)个时同段上, 两节内容相同的课程同时在不同的地点进行, 其中, 牛牛预先被安排在教室 ci上课, 而另一节课程在教室 di进行. 在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程.如果学生想更换第i节课程的教室,则需要提出中情.若申请通过,学生

BZOJ 4720 [Noip2016]换教室

4720: [Noip2016]换教室 Description 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程.在可以选择的课程中,有2n节课程安排在n个时间段上.在第i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室ci上课,而另一节课程在教室di进行.在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的n节安排好的课程.如果学生想更换第i节课程的教室,则需要提出申请.若申请通过,学生就可以在第i个时间段去教室

深入hibernate的三种状态(转)

学过hibernate的人都可能都知道hibernate有三种状态,transient(瞬时状态),persistent(持久化状态)以及detached(离线状态),大家伙也许也知道这三者之间的区别,比如瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态就是已经被保存到数据库中,离线状态就是数据库中有,但是session中不存在该对象.但是大家又是否对hibernate的session的那几个特殊方法一清二楚呢?或者说大家是否能够一眼就快速看出一个测试用例在反复的调用sess

深入hibernate的三种状态

学过hibernate的人都可能都知道hibernate有三种状态,transient(瞬时状态),persistent(持久化状态)以及detached(离线状态),大家伙也许也知道这三者之间的区别,比如瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态就是已经被保存到数据库中,离线状态就是数据库中有,但是session中不存在该对象.但是大家又是否对hibernate的session的那几个特殊方法一清二楚呢?或者说大家是否能够一眼就快速看出一个测试用例在反复的调用sess

简单理解Hibernate三种状态的概念及互相转化

本文描述了Hibernate三种状态的概念及互相转化.Java对象的生命周期中有三种状态,而且互相转化.它们分别是临时状态,持久化状态,以及游离状态. AD:WOT2015 互联网运维与开发者大会 热销抢票 在Hibernate中有三种状态,对它的深入理解,才能更好的理解hibernate的运行机理,刚开始不太注意这些概念,后来发现它是重要的.对于理解hibernate,JVM和sql的关系有更好的理解.对于需要持久化的JAVA对象,在它的生命周期中有三种状态,而且互相转化. Hibernate

java:Hibernate框架(环境搭建,Hibernate.cfg.xml中属性含义,Hibernate常用API对象,HibernteUitl,对象生命周期图,数据对象的三种状态)

1.环境搭建: 三个准备+7个步骤 准备1:新建项目并添加hibernate依赖的jar文件  准备2:在classpath下(src目录下)新建hibernate的配置文件:hibernate.cfg.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configurati

Hibernate三种状态,缓存,以及update更新问题

一. Hibernate中对象的三种状态 1. 瞬时状态(transient) 当我们通过Java的new关键字来生成一个实体对象时,这时这个实体对象就处于自由状态,此时该对象只是通过JVM获得了一块内存空间,还并没有通过Session对象的save()方法保存进数据库,因此也就还没有纳入Hibernate的缓存管理中,也就是说该对象现在还自由的游荡于Hibernate缓存管理之外.所以我们可以看出自由对象最大的特点就是,在数据库中不存在一条与它对应的记录. 瞬时状态特点: I: 不和 Sess

hibernate三种状态

学过hibernate的人都可能都知道hibernate有三种状态,transient(瞬时状态),persistent(持久化状态)以及detached(离线状态),大家伙也许也知道这三者之间的区别,比如瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态就是已经被保存到数据库中,离线状态就是数据库中有,但是session中不存在该对象.但是大家又是否对hibernate的session的那几个特殊方法一清二楚呢?或者说大家是否能够一眼就快速看出一个测试用例在反复的调用sess

Hibernate三种状态的区分,以及save,update,saveOrUpdate,merge等的使用 ----转----

Hibernate三种状态的区分,以及save,update,saveOrUpdate,merge等的使用 Hibernate的对象有3种状态,分别为:瞬时态(Transient). 持久态(Persistent).脱管态(Detached).处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object). 瞬时态         由new命令开辟内存空间的java对象, eg. Person person = new Person(