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);
}