P1850 [NOIP2016 提高组] 换教室
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1850 [NOIP2016 提高组] 换教室相关的知识,希望对你有一定的参考价值。
题意:
有2n个课安排在n个时间段上,每个时间段上都有两个一样的课同时在不同地方上,起初牛牛被所有课都被安排在Ci上课,另一节课在Di上课。牛牛现在想跟换到Di位置,它最多可以申请m节课换教室,对于每节课换成功的概率为P[i],每个教室之间都有距离,问申请哪几门课程可以使他在教室间的移动总和的期望值最小,输出这个最小值
注意申请教室是一次性的,无法根据其他课的申请结果来决定其他课程是否申请
题解:
按照一般的思路,我们会设dp[i][j]表示前i个课我们申请了j节,移动的最小期望值。dp[i][j] =min(dp[i-1][j-1]+p[i] * dis +dp[i-1][j]+(1-p[i]) * dis),我们可能会得到这样的方程,但是有个大问题,因为我们在转移过程中被认为是知道上一次申请的结果的,但是在题目中,所有申请是一次提交的,我们并不知道上一次申请会是什么样的结果,所以二维状态是不够用的。所以我们设dp[i][j][0/1],前两维意思相同,第三维表示我们假设第i个教室我们没有选择交换(交换是否成功未知,只是我们提交了申请)
那么我们转移会有考虑当前当前是否转移,以及上一次是否转移
转移情况如图:
详细见代码吧
代码:
调了半天不知道哪写错了,直接找了AC代码
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
typedef long long ll;
using namespace std;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){
ll s=0,w=1ll;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
return s*w;
}
const int maxn=2030;
double dp[maxn][maxn][3];
double dis[maxn][maxn];
int c[maxn],d[maxn];
double p[maxn];
int n,m,v,e;
void Floyd(){
for(int i=1;i<=v;i++){
for(int j=1;j<=v;j++){
for(int k=1;k<=v;k++){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
}
void init(){
for(int i=1;i<=v;i++){
for(int j=1;j<=v;j++){
dis[i][j]=1e9;
}
}
}
int main()
{
cin>>n>>m>>v>>e;
init();
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++)cin>>d[i];
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=1;i<=e;i++){
int u,v,w;
cin>>u>>v>>w;
dis[u][v]=w;
dis[v][u]=w;
}
Floyd();
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
dp[i][j][0]=dp[i][j][1]=1e9;
}
}
dp[1][0][0]=dp[1][1][1]=0; //初始化
for(int i=2;i<=n;i++){
for(int j=0;j<=min(m,i);j++){
dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]],
dp[i-1][j][1]+dis[d[i-1]][c[i]]*p[i-1]
+dis[c[i-1]][c[i]]*(1-p[i-1]));
if(j!=0)
dp[i][j][1]=min(
dp[i-1][j-1][0]
+dis[c[i-1]][d[i]]*p[i]
+dis[c[i-1]][c[i]]*(1-p[i]),
dp[i-1][j-1][1]+
dis[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])
+dis[c[i-1]][d[i]]*(1-p[i-1])*p[i]
+dis[d[i-1]][c[i]]*(1-p[i])*p[i-1]
+dis[d[i-1]][d[i]]*p[i-1]*p[i]);
}
}
double Dis=1e9;
for(int i=0;i<=m;i++)
Dis=min(min(dp[n][i][0],dp[n][i][1]),Dis);
printf("%.2f",Dis);
return 0;
}
AC代码
#include<iostream>
#include<cstdio>
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 ;
}
int main()
{
int n,m,v,e,a1,b1,c1;
cin>>n>>m>>v>>e;
for( int i=1;i<=n;i++)c[i]=qread();
for( int i=1;i<=n;i++)d[i]=qread();
for( int i=1;i<=n;i++)cin>>p[i];
for( int i=1;i<=v;i++)
for( int j=1;j<i;j++)
f[i][j]=f[j][i]=999999999;
for( int i=1;i<=e;i++){
a1=qread(),b1=qread(),c1=qread();
f[a1][b1]=f[b1][a1]=min(f[a1][b1],c1);
}
for( int k=1;k<=v;k++)
for( int i=1;i<=v;i++)
for( 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( int i=1;i<=n;i++)
for( 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( int i=2;i<=n;i++){
double add1=f[c[i-1]][c[i]];
for( 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);
}
以上是关于P1850 [NOIP2016 提高组] 换教室的主要内容,如果未能解决你的问题,请参考以下文章