LightOJ 1287 Where to Run 期望DP+状压+记忆化搜索

Where to Run LightOJ - 1287

给一个有 n n n 个结点, m m m 条边的无向图,从 0 0 0 出发,定义 E J EJ EJ 为未访问过的结点,并且通过该结点仍然能够遍历全图,假设当前结点的 E J EJ EJ 结点有 c n t cnt cnt 个,在当前结点时可以进行如下选择:

(1)在当前结点待 5 5 5 分钟
(2)以相同概率在 E J EJ EJ 中选一个结点走下去

当走到图中最后一个结点时被警察抓住,求被警察抓住的期望时间是多少?

因为 n ≤ 15 n\le 15 n15 ,因此可以考虑状态压缩,用 d p [ i ] [ s ] dp[i][s] dp[i][s] 表示当前结点是 i i i ,当前状态是 s s s 时遍历完全图的期望时间,其中 s s s 的二进制表示当前已经访问的结点。

E i \mathcal{E}_i Ei 为结点 i i i E J EJ EJ 集合, ∣ E i ∣ |\mathcal{E}_i| Ei 为集合大小,则

d p [ i ] [ s ] = 1 ∣ E i ∣ + 1 ( d p [ i ] [ s ] + 5 ) + 1 ∣ E i ∣ + 1 ∑ j ∈ E i ( d p [ j ] [ s ∣ ( 1 < < j ) ] + w [ i ] [ j ] ) d p [ i ] [ s ] = 5 ∣ E i ∣ + 1 ∣ E i ∣ ∑ j ∈ E i ( d p [ j ] [ s ∣ ( 1 < < j ) ] + w [ i ] [ j ] ) \begin{aligned} dp[i][s]&=\frac{1}{|\mathcal{E}_i|+1}(dp[i][s]+5)+\frac{1}{|\mathcal{E}_i|+1}\sum_{j\in\mathcal{E}_i}(dp[j][s|(1<<j)]+w[i][j])\\ dp[i][s]&=\frac{5}{|\mathcal{E}_i|}+\frac{1}{|\mathcal{E}_i|}\sum_{j\in\mathcal{E}_i}(dp[j][s|(1<<j)]+w[i][j])\\ \end{aligned} dp[i][s]dp[i][s]=Ei+11(dp[i][s]+5)+Ei+11jEi(dp[j][s(1<<j)]+w[i][j])=Ei5+Ei1jEi(dp[j][s(1<<j)]+w[i][j])

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
//#define WINE
#define MAXN 20
#define MAXM 300
using namespace std;
int T,iCase,n,m,cnt,u,v,w,head[MAXN];
double dp[MAXN][1<<15];
bool vis[MAXN],mem[MAXN][1<<15];
struct Edge{
    int to,w,nxt;
}edge[MAXM];
void addedge(int u,int v,int w){
    edge[cnt].to=v;
    edge[cnt].w=w;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
}
void dfs(int u,int s){
    if(mem[u][s])return ;
    int k=0;double sum=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to,w=edge[i].w;
        if(vis[v])continue;
        int ns=s|(1<<v);
        vis[v]=true;dfs(v,ns);vis[v]=false;
        if(mem[v][ns]){
            mem[u][s]=true;
            k++;sum+=dp[v][ns]+w;
        }
    }
    if(s==((1<<n)-1))mem[u][s]=true;
    if(k!=0)dp[u][s]=(5.0+sum)/k;
}
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d",&T);
    while(T--){
        memset(vis,false,sizeof(vis));
        memset(mem,false,sizeof(mem));
        memset(head,-1,sizeof(head));
        memset(dp,0,sizeof(dp));
        cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);addedge(v,u,w);
        }
        vis[0]=true;
        dfs(0,1);
        printf("Case %d: %.8lf\n",++iCase,dp[0][1]);
    }
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值