1.机器人行走问题
2.两人博弈问题
看上面这道题目,首先先用暴力递归的方法来写,暴力递归就是把机器人从m位置开始,经过k步的所有方法都列举出来,然后找出最终位置在p的点,数一数一共有多少这样的路径就是我们最终想要的答案。然后我们就开始自然尝试,会发现机器人在1位置的时候,其下一步只能去2号位置,当机器人到达n位置的时候,其下一步就只能去n-1这个位置,当机器人在其他位置的时候,可以往左走也可以往右走(自然尝试)。由这些想法之后我们就可以写出暴力递归的方法了。
public static void main(String[] args) {
// TODO Auto-generated method stub]
System.out.println("===========暴力递归========");
System.out.println("方法数为:"+MaxMethod(5,3,3,4));
System.out.println("============动态规划===========");
System.out.println("方法数为:"+Cmain(5,3,3,4));
}
public static int MaxMethod(int n,int k,int begin,int end) {
if(k==0)
return begin==end?1:0;
if(begin==1)
return MaxMethod(n,k-1,begin+1,end);
else if(begin==n)
return MaxMethod(n,k-1,begin-1,end);
else
return MaxMethod(n,k-1,begin+1,end)+MaxMethod(n,k-1,begin-1,end);
}
//根据暴力递归来改成动态规划
public static int Cmain(int n,int k,int begin,int end) {
if(n<1||k<0||begin<=0||begin>n|end<=0||end>n)
return -1;
int dp[][]=new int[n+1][k+1];
for(int i=0;i<=n;i++) {
for(int j=0;j<=k;j++)
dp[i][j]=-1;
}
return MaxMethod1(n,k,begin,end,dp);
}
public static int MaxMethod1(int n,int k,int begin,int end,int dp[][]) {
if(dp[begin][k]!=-1)
return dp[begin][k];
//之前没有算过
int ans=0;
if(k==0)
ans=begin==end?1:0;
else if(begin==1)
ans=MaxMethod1(n,k-1,2,end,dp);
else if(begin==n)
ans=MaxMethod1(n,k-1,begin-1,end,dp);
else
ans=MaxMethod1(n,k-1,begin+1,end,dp)+MaxMethod1(n,k-1,begin-1,end,dp);
dp[begin][k]=ans;
return ans;
}
那么怎样由暴力递归改成动态规划呢?
首先我们要明白暴力递归和动态规划他们两的的区别在哪,暴力递归的过程就是把所有可能存在的方法都枚举一遍,在枚举的过程中即使有些信息我们已经得知了,暴力递归还会再次计算。由于这个原因,暴力递归的时间复杂度就比较高,而动态规划就是把已经算过的数据直接放到数组中,等下次在用这个数据的时候不在计算,直接取值即可。
这个就是递归的过程,我们可以看到,有些过程会重复进行。
怎样由暴力递归改成动态规划呢?
只需要将dp数组中的每个元素设成一个不会用到的值(如-1),在递归的时候判断相应格子上的数是不是-1,如果不是,证明其已经计算过了,直接返回使用就行了。
如何判断dp表的大小?
就看一直在变的量的大小。比如这一题,k和M(begin)一直在变,那么定义一个int dp[][]=new int[n+1][k+1];大的表就行。
两个人纸牌博弈问题
这也是一道比较经典的dp题型了。
我们需要考虑的是先手和后手,因为两个人都绝顶聪明,所以两个人在选择纸牌的时候会考虑对方的得分情况,所以我们分别对先后和后手进行考虑。先手和后手都可以拿第一张牌,也可以拿最后一张牌
其不能为贪心策略(那张牌大选那一张),因为还要考虑接下来对手拿的牌。我们递归的策略就是先手拿第一张牌加上后手拿一张牌(后手拿的最优)剩下牌的最优和先手拿最后一张牌加上后手拿一张牌剩下牌的最优,算出这两种情况中最大的那种情况就是先手的最好得分。后手就是拿第一张牌或者最后一张牌,因为先手已经把最优的情况选择了,所以后手就只能选择这两种情况得分最小的那种情况。
其代码为:
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner srr=new Scanner(System.in);
//System.out.println("请你输入一串数字,中间用空格分开,");
int a[]= {1,23,34,54,65};
//先手做多得分
int p1=Fist_Play(a,0,4);
int p2=Secound_Play(a,0,4);
System.out.println("先手的最高得分"+p1);
System.out.println("后手的最高得分"+p2);
System.out.print("获胜者为:");
if(p1>p2)
System.out.println("先手");
else if(p2>p1)
System.out.println("后手");
else
System.out.println("平局");
//动态规划
System.out.println("---------动态规划--------");
System.out.println(Cmain(a));
}
//先手的拿牌情况
public static int Fist_Play(int a[],int left,int right) {
if(right==left)
return a[left];
else {
int p1=a[left]+Secound_Play(a,left+1,right);
int p2=a[right]+Secound_Play(a,left,right-1);
return Math.max(p1, p2);
}
}
public static int Secound_Play(int a[],int left,int right) {
if(right==left)
return 0;//因为剩余的那一张牌已经被先手拿走了,此时数组内没有数字
else{
int p1=Fist_Play(a,left+1,right);
int p2=Fist_Play(a,left,right-1);
return Math.min(p1, p2);
}
}
/*
* 由暴力递归改成动态规划需要注意三点
* 1.创建一个合适大小的dp表
* 2.找到这个dp表的初始值(看暴力递归的base,case)
* 3.填表的顺序(根据暴力递归递归的过程)
*/
public static int Cmain(int a[]) {
int number=a.length;
int fmap[][]=new int[number][number];
int gmap[][]=new int[number][number];
for(int i=0;ip2)
System.out.println("先手");
else if(p2>p1)
System.out.println("后手");
else
System.out.println("平局");
return Math.max(p1, p2);
}
public static int Fist_Play1(int a[],int left,int right,int dp1[][],int dp2[][]) {
/*
* 根据left和right判断出这个dp表的大小为[a.length][a.length]
* if(right==left)
* return a[left];
*根据这一句话来判断这个表初始值都哪些
*/
int k=0;
for(int i=0;i
根据第一题的提示,生成一个dp数组,将算过的数放到表中,当要使用表中数据的时候直接调用就行了。填表的时候这个题也比较独特,两张表相互依赖,先手表的对角线 全为第一个纸牌的值,后手的对角线全为0.然后再根据递归,找到依赖将表填完就行了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)