洛谷 P1850 换教室

终于有一道没有多组数据的题啦!

看了看题解,他们的状态转移都打得好长……
所以索性就不看了!
这其实就是个比较裸的期望DP
但是我太菜了以至于我忍不住看题解

首先跑一遍Floyd求全局最短路没有毛病
然后定义状态:
double f[i][j][k]为前 i i i堂课中请求换了 j j j堂,上学时每一天去到第 i i i堂课的教室后,总共已经走过的路程,其中 k = 1 k=1 k=1表示第 i i i堂课申请换课, k = 0 k=0 k=0表示没申请
int map[i][j]表示 i ⟶ j i\longrightarrow j ij或是 j ⟶ i j\longrightarrow i ji的距离
int c[i],d[i]如题意
double p[i]为题中k[i]
然后我们来推转移方程:

  1. 这个点没有申请
    没有申请的话 j j j就不会增加,但是距离什么的还得看前一堂课有没有申请
    f[i][j][0]

    1. 如果前一堂课有申请
      f[i-1][j][1]
      还得讨论
      1. 如果申请成功了
        map[ d[i-1] ] [ c[i] ](乘上几率)*p[i-1]
      2. 如果失败
        map[ c[i-1] ] [ c[i] ]*(1-p[i-1])
    2. 没有申请
      f[i-1][j][0]
      那就不用乘上几率了
      map[ c[i-1] ] [ c[i] ]

    前面这些连起来就是:
    f[i][j][0]=min(
    f[i-1][j][1] + map[ d[i-1] ][ c[i] ] * p[i-1] + map[ c[i-1] ][ c[i] ] * (1-p[i-1]),
    f[i-1][j][0] + map[ c[i-1] ][ c[i] ]
    )

  2. 申请了
    这下情况就复杂了,但是我们还是可以应对的
    f[i][j][1]

    1. 上一节课申请
      f[i-1][j-1][1]
      1. 两节都成功
        map[ d[i-1] ][ d[i] ] * p[i-1] * p[i]
      2. 上节成功这节失败
        map[ d[i-1] ][ c[i] ] * p[i-1] * (1-p[i])
      3. 上节失败这节成功
        map[ c[i-1] ][ d[i] ] * (1-p[i-1]) * p[i]
      4. 都失败
        map[ c[i-1] ][ c[i] ] * (1-p[i-1]) * (1-p[i])
    2. 没有
      f[i-1][j-1][0]
      1. 这节成功
        map[ c[i-1] ][ d[i] ] * p[i]
      2. 这节失败
        map[ c[i-1] ][ c[i] ] * (1-p[i])

    终于打完了!
    总结起来就有点长了:
    f[i][j][1]=min(
    f[i-1][j-1][1]+
    map[ d[i-1] ][ d[i] ] * p[i-1] * p[i]+
    map[ d[i-1] ][ c[i] ] * p[i-1] * (1-p[i])+
    map[ c[i-1] ][ d[i] ] * (1-p[i-1]) * p[i]+
    map[ c[i-1] ][ c[i] ] * (1-p[i-1]) * (1-p[i])
    ,
    f[i-1][j-1][0]+
    map[ c[i-1] ][ d[i] ] * p[i]
    map[ c[i-1] ][ c[i] ] * (1-p[i])
    )
    至此,转移方程推理全部他娘的结束
    (我终于明白为什么STL里面的函数传参表要分开几行还带着缩进打了)

然后就可以AC了
注意边界条件
顺带一提我一开始Floyd还打错了

#include
#include
using namespace std;
inline double min(const double a,const double b)
{return a<b?a:b;}
inline void read(int &x)
{
	x=0;register char c=getchar();
	while(c<48||57<c) c=getchar();
	for(;48<=c&&c<=57;c=getchar()) x=x*10+(c&15);
}
int map[310][310];
namespace graph
{
int n,m;
void graph()
{
	memset(map,63,sizeof(map));
	for(int i=1;i<=n;i++)
		map[i][i]=0;
	int x,y,c;
	for(int i=1;i<=m;i++)
	{
		read(x);read(y);read(c);
		map[y][x]=map[x][y]=min(map[x][y],c);
	}
	for(int k=1;k<=n;k++)//k要放最外面QAQ
		for(int i=1;i<=n;i++)
			if(i!=k)
				for(int j=1;j<=n;j++)
					if(i!=j&&k!=j)
						map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
}
};
int c[2100],d[2100];
double p[2100],f[2100][2100][2];
int main()
{
	int n,m;read(n);read(m);read(graph::n);read(graph::m);
	for(int i=1;i<=n;i++)
		read(c[i]);
	for(int i=1;i<=n;i++)
		read(d[i]);
	for(int i=1;i<=n;i++)
		scanf("%lf",p+i);
	graph::graph();
	memset(f,127,sizeof(f));//这是double的极大值
	f[1][0][0]=f[1][1][1]=0;
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<=min(i,m);j++)
		{
			if(j<i)f[i][j][0]=min(
				f[i-1][j][1]+map[d[i-1]][c[i]]*p[i-1]+map[c[i-1]][c[i]]*(1-p[i-1]),
				f[i-1][j][0]+map[c[i-1]][c[i]]
			);
			if(0<j)f[i][j][1]=min(
				f[i-1][j-1][1]+
					map[d[i-1]][d[i]]*p[i-1]*p[i]+
					map[d[i-1]][c[i]]*p[i-1]*(1-p[i])+
					map[c[i-1]][d[i]]*(1-p[i-1])*p[i]+
					map[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i]),
				f[i-1][j-1][0]+
					map[c[i-1]][d[i]]*p[i]+
					map[c[i-1]][c[i]]*(1-p[i])
			);
		}
	}
	double ans=1e308;
	for(int i=0;i<=m;i++)
		ans=min(ans,min(f[n][i][0],f[n][i][1]));
	printf("%.2lf",ans);
	return 0;
}

你可能感兴趣的:(做题笔记)