[机房测试]变异大老鼠
Description
给一棵树,逃犯初始在根,可以在每个点安一些人,安排 \(j\) 个人会有 \(p_{u,j}\) 的概率抓住逃犯。逃犯如果在一个节点没被抓住,就会等概率前往一个儿子,没有儿子逃犯就逃跑了。现在需分配这 \(K\) 个人,使得逃犯被抓住的概率最大。
Solution
本来是一个图的,要求出最短路径树才是上述题意,但没什么意思,所以省了。大样例是真的水。
很显然是一个树形dp,考虑 \(dp_{u,k}\) 表示逃犯当前在 \(u\),该子树一共安排 \(k\) 个人,能得到的最大概率。枚举当前节点分配多少人,就有
\[dp_{u,k}=\max\limits_{0\leq j\leq k} \{p_{u,j}+\frac{1-p_{u,j}}{d}\max\{\sum_{r_1+r_2+\dots+r_d=k-j}dp_{v,r}\}\}
\]
容易发现后面是一个背包,可以预处理出来,记为 \(f_{k-j}\)。那么 \(dp\) 就可以直接暴力更新了。
#include<stdio.h>
#include<algorithm>
#include<queue>
typedef double db;
using namespace std;
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
const int N=3e2+7;
const int M=3e4+7;
int n,m,K;
struct E{
int next,to,dis;
}e[M<<1];
struct Node{
int pos,val;
Node(int pos_=0,int val_=0):pos(pos_),val(val_){}
bool operator <(const Node &X) const{return val>X.val;}
};
bool vis[N];
priority_queue<Node> Q;
int dis[N],fa[N],cnt=0,head[N];
inline void add(int id,int to,int w=0){
e[++cnt]=(E){head[id],to,w};
head[id]=cnt;
e[++cnt]=(E){head[to],id,w};
head[to]=cnt;
}
db dp[N][N],f[N],p[N][N];
void dfs(int u){
int tot=0;
for(int i=head[u];i;i=e[i].next)
if(e[i].to!=fa[u]) dfs(e[i].to),tot++;
for(int i=0;i<=K;i++) f[i]=0;
for(int i=head[u];i;i=e[i].next){
const int v=e[i].to;
if(v==fa[u]) continue;
for(int j=K;j;j--)
for(int k=1;k<=j;k++)
f[j]=max(f[j],f[j-k]+dp[v][k]);
}
if(tot){
for(int i=1;i<=K;i++)
for(int j=0;j<=i;j++)
dp[u][i]=max(dp[u][i],p[u][j]+(1-p[u][j])/tot*f[i-j]);
}else for(int i=1;i<=K;i++) dp[u][i]=p[u][i];
}
int main(){
n=read(),m=read(),K=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add(u,v,read());
}
for(int i=1;i<=n;i++) dis[i]=1<<30;
dis[1]=0; Q.push(Node(1,0));
while(!Q.empty()){
Node t=Q.top(); Q.pop(); int u=t.pos;
if(vis[u]) continue; else vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].dis){
dis[v]=dis[u]+e[i].dis;
fa[v]=u; if(!vis[v]) Q.push(Node(v,dis[v]));
}
}
}
cnt=0; for(int i=1;i<=n;i++) head[i]=0;
for(int i=2;i<=n;i++) add(i,fa[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=K;j++) scanf("%lf",&p[i][j]);
dfs(1); printf("%.6lf",dp[1][K]);
}