A Walk Through the Forest HDU 1142
题目大意:有N个位置,办公室在1位置,家在2位置,给出M条双向边,现在需要从1到2,但是从A到B有一个条件就是,存在一条B到家的路径长度小于任意一条A到家的路径长度(即d[b]<d[a])d表示的是x到家的最短路径
第一次接触记忆化搜索这个东西,还是先感受一下大概的意思
1.斐波那契
非记忆化
ll F(ll x)
{
if (x==1||x==2)
return 1;
else
return F(x-1)+F(x-2);
}
记忆化
dp数组用来“记忆”F(x)的值,如果已经算过了,那么就可以直接拿来用,也不用再一层层递归了,拿个例子来讲,辛辛苦苦算了F(50)和F(51),如果要算F(52),用记忆化搜索直接答案就出来了dp[52]=dp[50]+dp[51],但是如果不用记忆化就得再进行好多好多次的递归.
memset(dp,0,sizeof dp);
ll F(ll x)
{
if (dp[x]!=0)
return dp[x];
if (x==1||x==2)
return 1;
return dp[x]=F(x-1)+F(x-2);
}
2.阶乘
非记忆化
ll fact(int x)
{
if (x==1)
return 1;
return x*fact(x-1);
}
记忆化
memset(dp,0,sizeof dp);
ll fact(ll x)
{
if (dp[x]!=0)
return dp[x];
if (x==1)
return 1;
return dp[x]=x*fact(x-1);
}
3.回归正题
我们现在可以先算出家(2)到其他点的最短距离,跑一个SPFA或者dijkstra即可,大不了套个板子就行了,然后用记忆化搜索的方法,dp[x]表示经过x一共有几条路径
int dfs(int x)
{
if (x==2)
return 1;//如果到达了2,那么就算是一条路径
if (dp[x]!=0)
return dp[x];//如果已经被算过了的话,就直接输出这个点的路径数量
int cnt=0;
for (int i=1; i<=n; i++)
{
if (cost[x][i]!=INF&&d[x]>d[i])
cnt+=dfs(i);//如果这个点是没被算过的,那么就便利一遍与它相连且符合条件的点
}
return dp[x]=cnt;//这个加和就是最终这个点的路径数量,记忆一下
}
找了一下网上大神的解释,感觉解释的很好啊
int dfs(int x)
{
if (dp[x]已经解得)
{
return dp[x];
}
else
{
初始化cnt;
将dp[x]分解成很多个小问题的加和形式k1,k2,……kn;
cnt+=dfs(k1)+dfs(k2)……;
return dp[x]=cnt;
}
}
总结一下,记忆化搜索可以减少一些不必要的计算,当数据范围比较大并且说有很多数据会重复计算的时候,用记忆化搜索可以大大增加效率
(虽然不知道下一次碰到能不能用上,但是至少稍微了解了一点菊部0.0)