lightoj 1287 Where to Run
感觉这个题的题面描述好坑啊,题目链接:lightoj1287
对于题目的理解是:由于需要时间尽可能长,那么需要尽可能的遍历全图
所以,最终的状态一定是从起点一笔画到了终点,中间无法走重复路径
这样的话,成了一个很经典的问题,n的数值很小,很明显是压缩dp解决这个概率问题
如何求该点的期望:
dp【u】=(5+dp【u】)/(cnt+1)+(mp【u】【i1】+mp【u】【i2】+……mp【u】【icnt】)/(cnt+1),其中,cnt是u合法连接的所有点数,mp【u】【ik】表示边数
如果cnt=0,说明该点是去不了的(看样例3),那么dp【u】=0
化简之后得到公式
设计状态:dp【u】【st】:为当前点在i点,状态为st的期望值(st为二进制压缩的,如st=0110,那么这个状态是去过1,2两个点的)
状态转移:
如果u和i相连那么mp【u】【i】>0
如果st状态中没有i这个点,即st&(1<<i)==0
如果st状态去了i点之后,可以到达终点遍历全图,即dp【i】【st|(1<<i)】>0
那么
dp【u】【st】+=dp【i】【st|(1<<i)】+mp【u】【i】
剩下的细节处理就是记忆化搜索需要做的事情了,代码如下:
int t,n,m;
double mp[maxn][maxn];
double dp[maxn][1<<maxn];
bool vis[maxn][1<<maxn];
int dfs(int st,int u){
if (st==(1<<n)-1){
dp[u][st]=0;
return 1;
}
if (vis[u][st]) return dp[u][st]>eps;
int cnt=0;
dp[u][st]=5;
vis[u][st]=true;
for(int i=0;i<n;i++)
if (mp[u][i]&&dfs(st|(1<<i),i)&&(!(st&(1<<i)))){
int st0=st|(1<<i);
cnt++;
dp[u][st]+=mp[u][i]+dp[i][st0];
}
if (cnt){
dp[u][st]/=cnt;
return 1;
}
dp[u][st]=0;
return 0;
}
int main(){
//input;
scanf("%d",&t);
for(int Case=1;Case<=t;Case++){
memset(dp,0,sizeof(dp));
memset(mp,0,sizeof(mp));
memset(vis,false,sizeof(vis));
scanf("%d%d",&n,&m);
int u,v,w;
while(m--){
scanf("%d%d%d",&u,&v,&w);
mp[u][v]=mp[v][u]=1.0*w;
}
dfs(1,0);
printf("Case %d: %.10lf\n",Case,dp[0][1]);
}
return 0;
}