T1
状态的定义
dp[i][j]表示从(i,j)出发到达目标点的期望移动步数(也有别的定义方法可以顺推什么的,这里不做解释)
答案
显而易见是dp[1][1]
初始化
dp[r][c]=0
转移方程
dp[i][j]=(dp[i][j+1]+2)*r[i][j]+(dp[i+1][j]+2)*d[i][j]+(dp[i][j]+2)*s[i][j]
分别表示向右,向下和留在原地的可能。
然后移个项得到下式
dp[i][j]=((dp[i][j+1]+2)*r[i][j]+(dp[i+1][j]+2)*d[i][j]+2*s[i][j])/(1-s[i][j]);
不过题目里似乎出现了虽然不是最后一个但是s[i][j]=1的情况所以说要特判一下…
#include<bits/stdc++.h>
#define db double
#define eps 1e-5
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (; c<'0'; c=getchar());
for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}
db dp[1005][1005],r[1005][1005],d[1005][1005],s[1005][1005];
const int o=0;
int main(){
int n,m,i,j;
for (; scanf("%d%d",&n,&m)!=EOF; ){
for (i=1; i<=n; i++)for (j=1; j<=m; j++)scanf("%lf%lf%lf",&s[i][j],&r[i][j],&d[i][j]);
memset(dp,0,sizeof(dp));
for (i=n; i; i--){
for (j=m; j; j--){
if (i==n&&j==m)continue;
if (fabs(s[i][j]-1)<eps)continue;
dp[i][j]=((dp[i][j+1]+2)*r[i][j]+(dp[i+1][j]+2)*d[i][j]+2*s[i][j])/(1-s[i][j]);
}
}
printf("%.3f\n",dp[1][1]);
}
return (o-o);
}
T2
大概是整个专题里最难的了…
首先每个点可能走到它的父亲节点或者1节点或者儿子节点。然后dp式子就有点儿复杂了。
定义
干脆点用dp[i]表示到达从i走出去的期望步数
答案
dp[1]
剩下具体内容见code
#include<bits/stdc++.h>
#define db double
#define M 10005
#define eps 1e-10
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (; c<'0'; c=getchar());
for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}
const int o=0;
db a[M],b[M],c[M],e[M],k[M];
int nx[M],ecnt,cnt[M];
struct edge{int x,nx;}ed[M<<1];
void add(int x,int y){
ed[ecnt]=(edge){y,nx[x]}; cnt[x]++;
nx[x]=ecnt++;
ed[ecnt]=(edge){x,nx[y]}; cnt[y]++;
nx[y]=ecnt++;
}
/*
dp[i] 表示从节点i成功走出的期望步数
对于叶子节点: dp[x]=0*e[x]+dp[1]*k[x]+(1-k[x]-e[x]+1)*dp[fa[x]]
=dp[1]*k[x]+(1-k[x]-e[x]+1)*dp[fa[x]]
对于非叶子节点: dp[x]=0*e[x]+dp[1]*k[x]+(1-k[x]-e[x])*(dp[fa]+dp[son[x][0]]+dp[son[x][1]]...+dp[son[x][m-1]]+E[x].size())/(E[x].size())
=dp[1]*k[x]+(1-k[x]-e[x])*(dp[fa]+dp[son[x][0]]+dp[son[x][1]]...+dp[son[x][m-1]])/(E[x].size())
然后题解有一步玄妙操作 对于每一个点x
dp[x]=a[x]*dp[1]+b[x]*dp[fa[x]]+c[x]
对于所有叶子节点
dp[x]=dp[1]*k[x]+(1-k[x]-e[x])*dp[fa[x]]
a[x]=k[x] b[x]=1-k[x]-e[x] c[x]=1-k[x]-e[x]
然后对于所有非叶子节点 直接推...
dp[1]=a[x]*dp[1]+b[x]*0+c[1]
答案是c[1]/(1-a[1])
*/
bool dfs(int f,int x){
db p=1.0/cnt[x];
a[x]=k[x];
b[x]=(1-k[x]-e[x])*p;
c[x]=1-k[x]-e[x];
int i,to;
db tmp=0;
for (i=nx[x];i;i=ed[i].nx)if (ed[i].x!=f){
to=ed[i].x;
if (!dfs(x,to))return 0;
a[x]+=(1-k[x]-e[x])*p*a[to];
c[x]+=(1-k[x]-e[x])*p*c[to];
tmp+=(1-k[x]-e[x])*p*b[to];
}
if (fabs(1-tmp)<eps)return 0;
a[x]/=(1-tmp);
b[x]/=(1-tmp);
c[x]/=(1-tmp);
return 1;
}
int main(){
int t,ca=0,n,i,j,x,y,z,K,E;
read(t);
for (; t; t--){
read(n);
memset(nx,0,sizeof(nx)); memset(cnt,0,sizeof(cnt));
ecnt=1;
for (i=1; i<n; i++){read(x); read(y); add(x,y);}
for (i=1; i<=n; i++){read(K); read(E); k[i]=K/100.0; e[i]=E/100.0;}
printf("Case %d: ",++ca);
if (dfs(0,1)&&fabs(1-a[1])>eps){printf("%.6f\n",c[1]/(1-a[1]));}
else printf("impossible\n");
}
return (o-o);
}
总之就是很厉害很神奇…如果比赛的时候遇见的话,大概是尽可能推式子然后听天命…
之后的题目注解基本都写在代码里了所以不会太多解释。
T3
大概就是分析一下哪种格子能走到哪种格子就好了。
0 1 2分别表示左脚,右脚和双脚都行
#include<bits/stdc++.h>
#define db double
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (; c<'0'; c=getchar());
for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');
}
/*
大概就是有四种格子,然后每种格子出现的概率随机而且不一样。
不过这不重要(x)
0什么都不能干
1->2
1->3
2->1
2->3
3->1
3->2
*/
db dp[4005][4],p[4005][4];
const int o=0;
int main(){
int t,n,a,b,i,j;
read(t);
for (;t;t--){
read(n); read(a); read(b);
memset(p,0,sizeof(p));
for (i=1; i<=n; i++)scanf("%lf%lf%lf%lf",&p[i][0],&p[i][1],&p[i][2],&p[i][3]);
for (i=n+1; i<=n*2; i++)p[i][3]=1;
memset(dp,0,sizeof(dp));
dp[n][1]=dp[n][2]=dp[n][0]=1;
for (i=n-1; i>=0; i--){
db tmp0=1,tmp1=1,tmp2=1;
for (j=i+a; j<=i+b; j++){
dp[i][0]+=tmp0*(p[j][2]*(dp[j][1]+1.0)+p[j][3]*(dp[j][2]+1.0));
dp[i][1]+=tmp1*(p[j][1]*(dp[j][0]+1.0)+p[j][3]*(dp[j][2]+1.0));
dp[i][2]+=tmp2*(p[j][3]*(dp[j][2]+1.0)+p[j][1]*(dp[j][0]+1.0)+p[j][2]*(dp[j][1]+1.0));
tmp0*=(p[j][0]+p[j][1]);
tmp1*=(p[j][0]+p[j][2]);
tmp2*=p[j][0];
}
}
printf("%.8f\n",dp[0][2]);
}
return (o-o);
}
好了哪天回来再补吧…。