P4206 [NOI2005] 聪聪与可可——期望,记搜
思路
初见思路就是没有思路
看了
暴力
注意到没有边权,可以用bfs求出点两两之间的距离
由于
然后再考虑聪聪会跳到的点
可以想到直接把会跳到的点存起来,计算时间是
接下来就可以暴搜了,可以写成返回值的模式就尽量写成这样,如果后面想到记忆化可以用上
最初是想
于是就可以暴力拿到
记搜
考虑优化搜索,当然目前三个参数的搜索是记忆化不了的
看看哪个可以压掉
答案是时间维,因为在转移过程中用到的时间流逝可以加在返回值所在的表达式里(此时状态已经发生改变了,所以不太好想)
也就是原先的 dfs(mouse,time+1,cat) 变成 dfs(mouse,cat)+1 ,这样就可以压掉一维了
于是就可以愉快地记忆化了
(话说回来我好像就没想过dp,这是一个问题)
也就是说如果一个状态在转移过程中并没有和其他的环境产生紧密的联系,那么它就可能是可以被压掉的那个量
AC Code:
#include <bits/stdc++.h>
#define db double
using namespace std;
const int MAXN=1e3+5;
const db eps=1e-17;
int nxt[MAXN][MAXN],dis[MAXN][MAXN];
db dp[MAXN][MAXN];
vector<int > edge[MAXN];
bool vis[MAXN];
list<int > q;
int n,m;
void bfs(int s){
memset(vis,0,sizeof(vis));
dis[s][s]=0;
q.push_back(s);
int u;vis[s]=1;
while(!q.empty()){
u=q.front();q.pop_front();
for(auto v:edge[u]){
if(vis[v]) continue;
dis[s][v]=dis[s][u]+1;
vis[v]=1;
q.push_back(v);
}
}
}//
int geted(int u,int c){
if(nxt[c][u]) c=nxt[c][u];
else{
if(c==u) return u;
for(auto v:edge[c])
if(dis[v][u]==dis[c][u]-1){nxt[c][u]=v;c=v;break;}
}
if(nxt[c][u]) c=nxt[c][u];
else{
if(c==u) return u;
for(auto v:edge[c])
if(dis[v][u]==dis[c][u]-1){nxt[c][u]=v;c=v;break;}
}
return c;
}//
db dfs(int u,int c){
if(dis[u][c]<=2) return 0.0;
if(abs(dp[u][c])>=eps) return dp[u][c];
int cc=c;
c=geted(u,c);
db res=0.0;
for(auto v:edge[u]){
if(c==v) continue;
else res+=(dfs(v,c)+1.0)/(db)(edge[u].size()+1.0);
}
res+=(dfs(u,c)+1.0)/(db)(edge[u].size()+1.0);
dp[u][cc]=res;
return res;
}//
int cat,mouse;
void work(){
scanf("%d%d",&n,&m);
scanf("%d%d",&cat,&mouse);
while(m--){
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i=1;i<=n;++i){
sort(edge[i].begin(),edge[i].end());
bfs(i);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
dp[i][j]=0.0;
printf("%.03lf",dfs(mouse,cat)+1);
}//
int main(){
work();
return 0;
}