Loj-1287 Where to Run(状压期望DP)
题目链接:LightOJ - 1287
题意:
先给一个n个点m条无向边的图,每条边都有一个权值,顶点编号从0开始,刚开始自己站在0号点,现在要躲避警察的追踪。走的路线有如下限制:
-
离开一个顶点之后就不能回到该顶点
-
如果站在某个顶点u,剩下的未走的邻接顶点不存在 E J 顶点就停止并被警察抓到,E J 顶点指如果到该顶点后,满足条件1的情况下可以走完所有未走完的顶点。
如果存在E J顶点,则会进行如下随机选择,如果有k个E J顶点,那么有1/(k+1)的概率选择先停留原地5分钟(隐藏)或走剩下的E J 顶点。
-
如果走u到v这条边,则花费的时间为u到v这条边的权值。
求从 0 0 0 号顶点开始出发躲避警察的时间的期望值。
数据范围: n ∈ [ 1 , 15 ] , 0 ≤ u , v < n , 0 < w ≤ 100 n\in[1,15],0\le u,v<n,0< w\le 100 n∈[1,15],0≤u,v<n,0<w≤100
思路:
我们用 d p [ s ] [ u ] dp[s][u] dp[s][u]代表经过的顶点状态是 s s s ,现在处于节点 u u u 的剩下的期望躲避时间,所以我们要求的就是 d p [ 1 ] [ 0 ] dp[1][0] dp[1][0]
期间我一直在想怎么判断剩下的邻接顶点是E J顶点,期间想了各种方法,但都太麻烦且时间复杂度大。
实际上对于状态 ( s , u ) (s,u) (s,u),我们用 c a n s [ s ] [ u ] cans[s][u] cans[s][u]代表这个状态下从 u u u 开始能否遍历完其他未遍历完的顶点,判断状态 ( s , u ) (s,u) (s,u)中 u u u是否为为E J顶点只需判断可选的顶点中是否存在E J 顶点即可
形式化的,我们现在来求 c a n s [ s ] [ u ] cans[s][u] cans[s][u],假设有边 ( u , v ) (u,v) (u,v),且状态 s s s中未经过顶点 v v v,那么如果 c a n s [ s ∣ ( 1 < < v ) ] [ v ] = t r u e cans[s|(1<<v)][v]=true cans[s∣(1<<v)][v]=true,则 c a n [ s ] [ u ] = t r u e can[s][u]=true can[s][u]=true
对于状态转移方程
假设状态 ( s , u ) (s,u) (s,u)可选的顶点有 k k k 个
-
如果 k > 0 k>0 k>0:
对于状态 ( s , u ) (s,u) (s,u)下u的所有邻接E J顶点 v v v,且边权 w w w,那么
d p [ s ] [ u ] = 1 k + 1 ∗ ( d p [ s ] [ u ] + 5 ) + 1 k + 1 ∗ ∑ k ( d p [ s ∣ ( 1 < < v ) ] [ v ] + w ) dp[s][u]=\frac{1}{k+1}*(dp[s][u]+5)+\frac{1}{k+1}*\sum _k (dp[s|(1<<v)][v]+w) dp[s][u]=k+11∗(dp[s][u]+5)+k+11∗∑k(dp[s∣(1<<v)][v]+w)
c a n s [ s ] [ u ] = t r u e cans[s][u]=true cans[s][u]=true
-
否则:
d p [ s ] [ u ] = 0 dp[s][u]=0 dp[s][u]=0
c a n s [ s ] [ u ] = f a l s e cans[s][u]=false cans[s][u]=false
因为 d p dp dp方程中 s s s 是递增的,所以我们可以按照 s s s 从大到小遍历,其次遍历 u u u 求 d p [ s ] [ u ] dp[s][u] dp[s][u]和 c a n s [ s ] [ u ] cans[s][u] cans[s][u]即可,特殊的满状态下 d p [ s m a x ] [ u ] = 0 , c a n s [ s m a x ] [ u ] = t r u e dp[s_{max}][u]=0,cans[s_{max}][u]=true dp[smax][u]=0,cans[smax][u]=true
代码:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
const double eps=1e-5;
typedef pair<int,int> P;
bool cans[1<<16][16];
double dp[1<<16][16];
vector<pair<int,int> > g[16];
int main()
{
int t,cas=0;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0; i<=n; ++i) g[i].clear();
for(int i=1; i<=m; ++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].push_back(make_pair(v,w));
g[v].push_back(make_pair(u,w));
}
int top=(1<<n)-1;
for(int s=top; s>=0; --s)
{
for(int u=0; u < n; ++u)
{
dp[s][u]=0;
cans[s][u]=false;
if(s==top)
{
cans[s][u]=true;
continue;
}
if((s&(1<<u)) == 0) continue;
int k=0;
double sum=0.0;
for(P &p:g[u])
{
int v=p.first,w=p.second;
if((s&(1<<v))==0&&cans[s|(1<<v)][v])
{
k++;
sum+=dp[s|(1<<v)][v]+w;
}
}
if(k > 0)
{
cans[s][u]=true;
dp[s][u]=(5.0+sum)/k;
}
}
}
printf("Case %d: %.8f\n",++cas,dp[1][0]);
}
return 0;
}