打家劫舍1

1.递归算法
一种最容易想到的,用不断递归的方法来解决的低效算法:
#include<stdio.h>
#include<stdlib.h>
//深搜解决打家劫舍问题
int dp(int start,int n,int nums[])
{
if(start>=n)
return 0;
return dp(start+1,n,nums)>=nums[start]+dp(start+2,n,nums)?dp(start+1,n,nums):nums[start]+dp(start+2,n,nums);
}
int rob(int n,int nums[])
{
return dp(0,n,nums);
}
main()
{
int nums[]={2,7,9,3,1};
int n=sizeof(nums)/sizeof(int);
int max=rob(n,nums);
printf("the max money we can rob is %d",max);
}
即对每一种问题都进行考虑:
在走每一步之前我们都面临两个选择:
1.抢劫当前这一家,然后对下下一家进行考虑
2.不抢劫当前这一家,对下一家进行考虑
“考虑”是对算法的递归调用,我们每次考虑都在两种选择中选择抢劫钱数最多的一个选择。而对每一种选择我们都需要进一步展开对他们进行考虑,直到出现一个截至条件,就像是深搜递归的终止条件一样。
在上述算法中,我们默认是从第0家开始考虑的,所以当我们对考虑的展开考虑,也就是对子问题的求解再次循环进行到第0家时,就应该对这样的考虑问题进行终止了。并且这样的考虑是没有意义的,我们将撤销这样的步骤,所以应当返回0,从而对结果不产生任何影响。
这样的算法是可行的,但是面临重复计算的低效问题。
即对于每一种考虑的结果,我们可能在无意识的情况下对其进行了大量重复计算,比如说对最优解的考虑我们可以初步展开成:
max(dp(1),n[0]+dp(2)) 而dp(1)又可以展开成max(dp(2),n[1]+dp(3))
到这里就足以说明重复计算的情况了,即在对最优解的考虑中我们需要对dp(2)的情况进行一次计算,而对dp(1)的考虑中,我们也要对dp(2)进行一次计算,这里的计算显然是重复的,并且由于对dp(2)的计算可能可以进一步进行大量的展开,其中又可能包含了大浪的重复计算,所以这就造成了算法效率的低下。
2.记忆化递归算法
我们应该使用记忆化的思想对这样的算法进行优化,即通过先将最优解的问题逐步拆分成子问题的解,然后不断求解子问题,并对子问题的结果进行记录,对其记录是很重要的,这样的记录可以避免对相同子问题的重复计算,从而显著提高散发效率,而我们只牺牲了很小一部分的空间复杂度。
#include<stdio.h>
#include<stdlib.h>
//动态规划处理打家劫舍
int dp(int start,int n,int nums[],int memory[])
{
if(start>=n)
return 0;
if(memory[start]!=0) return memory[start];
else memory[start]=dp(start+1,n,nums,memory)>=nums[start]+dp(start+2,n,nums,memory)?
dp(start+1,n,nums,memory):nums[start]+dp(start+2,n,nums,memory);
return memory[start];
}
int rob(int n,int nums[],int memory)
{
return dp(0,n,nums,memory);
}
main()
{
int nums[]={2,7,9,3,1};
int n=sizeof(nums)/sizeof(int);
//定义记录子问题的数组
int memory[n];
memset(memory,0,sizeof(memory));
int max=rob(n,nums,memory);
printf("the max money we can rob is %d",max);
}
3.深搜求解
深搜求解本问题的实质和第一种递归差不多,旨在求出每一种可能的情况且每一次搜索都将递归进行到尽头。
但是以下算法更贯彻了深搜的思想,每次一搜索的下次一搜索都直接对从下一个可搜索的点到最后一个可搜索的点在内的全部点展开了搜索,可以看作是自顶向下的搜索。并且对最优解的获取直接是在每次一搜索的结尾进行判断的,而没有将最优解分割为子最优解。
#include<stdio.h>
#include<stdlib.h>
int max=0;
int sum=0;
//深搜处理打家劫舍
void dfs(int index,int n,int nums[])
{
sum+=nums[index];
int i,j,k;
for(i=index+2;i<n;i++)
{
dfs(i,n,nums);
}
if(index+2>=n)
{
max = max<sum ? sum : max;
}
sum-=nums[index];
}
void rob(int n,int nums[])
{
dfs(0,n,nums);//偷第0家
dfs(1,n,nums);//不偷第0家
}
main()
{
int nums[]={2,7,9,3,1};
int n=sizeof(nums)/sizeof(int);
rob(n,nums);
printf("the max money we can rob is %d",max);
}

428

被折叠的 条评论
为什么被折叠?



