P4316 绿豆蛙的归宿 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 概率
dp[i]表示点i到终点的期望距离,因为只知道终点的期望所以往前推,假设知道了点u的期望,点v有一条指向点u的边,那么dp[v]+=(dp[u]+dis[v][u])/siz[v],siz[v]就是v指向了多少个点,siz[z]可以预处理出来,为了更好的搜索所以建一个反图,然后从点n开始跑就行了,注意的一点就是当点v指向的点都算出期望了,才能将v加入队列,有点拓扑排序的味道
ll siz[100005],n,m;
double dp[100005];
ll cnt,head[200005];
struct Edge{
ll from,next,to;
double dis;
}edge[200005];
void addedge(ll from,ll to,double dis){
edge[++cnt].to=to;
edge[cnt].next=head[from];
edge[cnt].dis=dis;
head[from]=cnt;
}
ll vis[100005],out[100005];
void bfs(){
queue<ll>q;q.push(n);dp[n]=0;
while(!q.empty()){
ll u=q.front();q.pop();
for(int i=head[u];i;i=edge[i].next){
ll j=edge[i].to;
dp[j]+=(dp[u]+edge[i].dis)/siz[j];
//cout<<j<<" "<<dp[j]<<" "<<u<<" "<<dp[u]<<" "<<edge[i].dis<<" "<<siz[j]<<endl;
out[j]--;
if(out[j]<=0) q.push(j);
}
}
printf("%.2lf\n",dp[1]);
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) siz[i]=0;
for(int i=1;i<=m;i++){
ll u,vv;
double w;
scanf("%lld%lld%lf",&u,&vv,&w);
siz[u]++;
out[u]++;
addedge(vv,u,w);
}
bfs();
return 0;
}
P1850 [NOIP2016 提高组] 换教室 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 概率dp
dp[i][j][0/1]表示已经申请了j个第i个时间段有没有发送申请,转移方程有点小坑
dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][0]+g[c[i-1]][c[i]]);
dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][1]+g[c[i-1]][c[i]]*(1-p[i-1])+g[d[i-1]][c[i]]*p[i-1]);
if(j!=0){
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][0]+g[c[i-1]][c[i]]*(1-p[i])+g[c[i-1]][d[i]]*p[i]);
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][1]+g[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+g[d[i-1]][c[i]]*p[i-1]*(1-p[i])+g[c[i-1]][d[i]]*(1-p[i-1])*p[i]+g[d[i-1]][d[i]]*p[i-1]*p[i]);
}
可以看到dp[i-1][j][1]是有两段的,一个是代表申请成功了,一个是申请没有成功,因为成功是有概率的,只要注意了这个就没啥难的了,虽然自己调代码跳了半天,,,
题解 P1850 【换教室】 - 笨蛋花的小窝qwq - 洛谷博客
ll c[2005],d[2005],n,m,v,e;
double p[2005],dp[2005][2005][2],g[305][305];
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&v,&e);
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
if(i==j) g[i][j]=0;
else g[i][j]=inf;
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) scanf("%lld",&d[i]);
for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
for(int i=1;i<=e;i++){
ll u,vv;
double w;
scanf("%lld%lld%lf",&u,&vv,&w);
g[u][vv]=g[vv][u]=min(g[u][vv],w);
}
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
g[i][j]=min(g[i][k]+g[k][j],g[i][j]);
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
dp[i][j][0]=dp[i][j][1]=inf;
dp[1][0][0]=dp[1][1][1]=0;
for(ll j=0;j<=m;j++)
for(ll i=j;i<=n;i++){
dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][0]+g[c[i-1]][c[i]]);
dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][1]+g[c[i-1]][c[i]]*(1-p[i-1])+g[d[i-1]][c[i]]*p[i-1]);
if(j!=0){
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][0]+g[c[i-1]][c[i]]*(1-p[i])+g[c[i-1]][d[i]]*p[i]);
dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][1]+g[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i])+g[d[i-1]][c[i]]*p[i-1]*(1-p[i])+g[c[i-1]][d[i]]*(1-p[i-1])*p[i]+g[d[i-1]][d[i]]*p[i-1]*p[i]);
}
}
double ans=1e18;
for(int i=0;i<=m;i++) ans=min({ans,dp[n][i][0],dp[n][i][1]});
printf("%.2lf\n",ans);
return 0;
}
1437D - Minimal Height Tree
题目给的也就是层序遍历树,每个点的孩子编号都是升序的,那我们就记录一下上一层有多少个点,遍历该层时如果a[i]>a[i+1]那么就转到下一个点,没有下一个点就只能转到下一层,las表示上一层有多少点,cur表示这一层有多少点,ch表示已经占用的上一层的点的个数
ll t,n,a[200005];
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
ll ans=1,las=1,ch=1,cur=1;
for(int i=2;i<n;i++)
if(a[i]>a[i+1]){
ch++;
if(ch>las){las=cur,ch=1,cur=1;ans++;}
else cur++;
}
else cur++;
printf("%lld\n",ans);
}
return 0;
}