洛谷题单:【算法1-4】递推与递归

(1)P1255 数楼梯
很经典的一道入门递归题。递推公式为f(n)=f(n-1)+f(n-2)。这里数据量很大,要用大数进行递归

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		BigInteger b=new BigInteger("1");
		BigInteger a=new BigInteger("2");
		BigInteger bi;
		for(int i=3;i<=n;i++) {
			bi=new BigInteger(a.add(b).toString());
			b=a;
			a=bi;
		}
		if(n==0) {
			System.out.println(0);
		}
		else if(n==1) {
			System.out.println(b);
		}
		else {
			System.out.println(a);
		}
		
		
	}
}

(2)P1002 过河卒
一开始肯定以为是用dfs做,因为搜索路径的题目很多都是用dfs做的。然而这道题实际是用动态规划。转移方程不难推出是dp[i][j]=dp[i-1][[j]+dp[i][j-1]。dp代表的是到达该点的路径数目,因为它可能从上面和左边过来,所以有了这个状态转移方程。
dfs做法:

import java.util.Scanner;

public class Main {
	static int[] dx= {0,-2,-1,1,2,-2,-1,1,2};
	static int[] dy= {0,-1,-2,-2,-1,1,2,2,1};
	static int n,m,x2,y2,sum=0;
	static boolean[][] vis;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();m=sc.nextInt();x2=sc.nextInt();y2=sc.nextInt();
		vis=new boolean[n+1][m+1];
	//	vis[0][0]=true;
		dfs(0,0);
		System.out.println(sum);
	}
	public static void dfs(int x,int y) {
		if(x==n&&y==m) {
			sum++;
			return;
		}
		vis[x][y]=true;
		if(x+1<=n&&!vis[x+1][y]&&judge(x+1,y)) {
			vis[x+1][y]=true;
			dfs(x+1,y);
			vis[x+1][y]=false;
		}
		if(y+1<=m&&!vis[x][y+1]&&judge(x,y+1)) {
			vis[x][y+1]=true;
			dfs(x,y+1);
			vis[x][y+1]=false;
		}
	}
	public static boolean judge(int x,int y) {
		for(int i=0;i<9;i++) {
			int tx=x2+dx[i];
			int ty=y2+dy[i];
			if(tx==x&&ty==y)return false;
		}
		return true;
	}
}

dp做法:

import java.util.Scanner;

public class Main {
	static int[] dx= {0,-2,-1,1,2,-2,-1,1,2};
	static int[] dy= {0,-1,-2,-2,-1,1,2,2,1};
	static int n,m,x2,y2;
	static boolean[][] vis;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();m=sc.nextInt();x2=sc.nextInt();y2=sc.nextInt();
		long[][] dp=new long[n+1][m+1];
		dp[0][0]=1;
		for(int i=0;i<=n;i++) {
			for(int j=0;j<=m;j++) {
				if(i==0&&j==0) {
					continue;
				}
				if(judge(i-1,j)&&judge(i,j-1))dp[i][j]=dp[i-1][j]+dp[i][j-1];
				else if(!judge(i-1,j)&&judge(i,j-1))dp[i][j]=dp[i][j-1];
				else if(judge(i-1,j)&&!judge(i,j-1))dp[i][j]=dp[i-1][j];
				else dp[i][j]=0;
			}
		}
		System.out.println(dp[n][m]);
	}
	public static boolean judge(int x,int y) {
		
		for(int i=0;i<9;i++) {
			int tx=x2+dx[i];
			int ty=y2+dy[i];
			if(tx==x&&ty==y)return false;
		}
		return 0<=x&&x<=n&&0<=y&&y<=m;
	}
}

(3)P1044 栈
和上题一样,可以用dfs来做,用Java自带的栈数据结构来模拟出入栈的情况。如果所有元素都出栈,计数加1,代表这种情况已经统计了。

import java.util.Scanner;
import java.util.Stack;

public class Main {
	static Stack<Integer> s=new Stack<Integer>();
	static int n,all=0;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		dfs(0,0,n); 
		System.out.println(all);
	}
	//sum代表栈里的元素个数,go代表出栈的元素个数,now代表还未入栈的元素个数
	public static void dfs(int sum,int go,int now) {
		if(go==n) {
			all++;
			return;
		}
		if(now>0) {
			s.push(1);
			dfs(sum+1,go,now-1);
			s.pop();
		}
		if(sum>0) {
			s.pop();
			dfs(sum-1,go+1,now);
			s.push(1);
		}
	}
	
}

但这种情况因为太多重复的递归会导致过不了所有测试点,所以这里可以改成记忆化递归。

import java.util.Scanner;
import java.util.Stack;

public class Main {
	static Stack<Integer> s=new Stack<Integer>();
	static int n;
	static int[][] f;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		f=new int[n+1][n+1];
		System.out.println(dfs(0,0,n)); 
	}
	//sum代表栈里的元素个数,go代表出栈的元素个数,now代表还未入栈的元素个数
	public static int dfs(int sum,int go,int now) {
		if(go==n) {
			return 1;
		}
		if(f[sum][now]!=0) {
			return f[sum][now];
		}
		if(now==0) {
			f[sum][now]=dfs(sum-1,go+1,now);
		}
		else if(sum==0) {
			f[sum][now]=dfs(sum+1,go,now-1);
		}
		else {
			f[sum][now]=dfs(sum+1,go,now-1)+dfs(sum-1,go+1,now);
		}
		return f[sum][now];
	}
	
}

当然这题也可以用动态规划来做,当当 i个数进栈,j−1 个数出栈的时候,只要再出一个数,便是i个数进栈,jj个数出栈的情况,同理,对于进栈 i−1 个数,出栈 j个数,在进栈一个数便是f[i,j]f[i,j]了,于是就有了递归式:f[i,j]=f[i-1,j+1]f[i,j]=f[i−1,j+1].代码就不再给出了。

(4)P1028 数的计算
又是一道递归题目,同样也是用记忆化递归进行优化,因为左边的数不能超过自身的一半,而且自身也算一种情况。我们就设置一个变量sum=1,再去递归的加上左边数字的情况。

import java.util.Scanner;

public class Main{
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		System.out.println(dfs(n));
	}
	static int[] arr=new int[1010];
	public static int dfs(int n) {
		if(arr[n]!=0)
			return arr[n];
		int sum=0;
		sum++;
		for(int i=n/2;i>0;i--) {
			sum+=dfs(i);
		}
		return arr[n]=sum;
		
	}
}

(5)P1464 Function
按照题目要求写一个函数,题目已经提示的非常明显了。递归的层数会非常多,肯定要我们加个记忆化递归呀。当然,不用开题目给出的那么大数字的数组,肯定开不下,当abc有一个超过20的时候,就直接当作20处理,所有开20大小的三维数组即可。

import java.util.Scanner;

public class Main {
	static long[][][] f=new long[30][30][30]; 
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		while(true) {
			Long a=sc.nextLong(),b=sc.nextLong(),c=sc.nextLong();
			if(a==-1&&b==-1&&c==-1) {
				return;
			}
			System.out.println("w("+a+", "+b+", "+c+") = "+w(a,b,c));
			
		}
	}
	public static long w(long a,long b,long c) {
		if(a<=0||b<=0||c<=0)
			return 1;
		else if(a>20||b>20||c>20)
			return w(20,20,20);
		else if(f[(int) a][(int) b][(int) c]!=0)
			return f[(int) a][(int) b][(int) c];
		else if(a<b&&b<c) {
			f[(int) a][(int) b][(int) c]=w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
			return f[(int) a][(int) b][(int) c];
					}
		else 
		{
			f[(int) a][(int) b][(int) c]=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
			return f[(int) a][(int) b][(int) c];}
		}
}

(6)
(7)P2437 蜜蜂路线
很明显的斐波拉契数列嘛。只不过现在不是从1开始。而从m开始,那我们把m当作1来处理是一样的。这里要用记忆化递归和大数。

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
	static BigInteger[] arr=new BigInteger[1010];
	static int m;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		m=sc.nextInt();
		int n=sc.nextInt();
		for(int i=0;i<arr.length;i++) {
			arr[i]=BigInteger.valueOf(0);
		}
		System.out.println(f(n));
		
	}
	public static BigInteger f(int n) {
		if(n==m) {
			return BigInteger.valueOf(1);
		}
		if(n==m+1)
			return BigInteger.valueOf(1);
		if(!arr[n].equals(BigInteger.valueOf(0))) {
			return arr[n];
		}
		arr[n]=f(n-1).add(f(n-2));
		return arr[n];
	}
}

(8)P1164 小A点菜
很明显的一道01背包问题,不过这里并不是求最大价值,而是求刚好装满的情况。所以这里dp[i][j]应该表示的是前i件物品在有j元的情况下能刚好花完的方案数目。状态转移方程有三种情况:当a[i]>j时,即钱不够买这个物品时,dp[i][j]=dp[[i-1][j],很好理解就是说有这件物品和没有这件物品都是一样的,因为买不了。第二种情况,当a[i]=j时,dp[i][j]=dp[i-1][j]+1,刚好能买就是一种情况,所以在之前的方案数加1,第三种情况,当a[i]<j时,那么我们就要考虑买这个物品剩的钱有几种刚好花完的情况 ,dp[i][j]=dp[i-1][j]+dp[i-1][j-arr[i]]。

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt(),m=sc.nextInt();
		int[][] dp=new int[n+1][m+1];
		int[] arr=new int[n+1];
		for(int i=1;i<=n;i++) {
			arr[i]=sc.nextInt();
		}
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=m;j++) {
				if(arr[i]>j) {
					dp[i][j]=dp[i-1][j];
				}
				else if (arr[i]==j){
					dp[i][j]=dp[i-1][j]+1;
				}
				else {
					dp[i][j]=dp[i-1][j]+dp[i-1][j-arr[i]];
				}
			}
		}
		System.out.println(dp[n][m]);
	}
}

(9)P1036 选数
在暴力枚举那个题单中出现过的题目,就是一个搜索,推出所有的情况,然后判断是否为素数。

import java.util.Scanner;

public class Main {
	static int n,k;
	static int[] arr;
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		n=sc.nextInt();
		k=sc.nextInt();
		arr=new int[n];
		for(int i=0;i<n;i++) {
			arr[i]=sc.nextInt();
		}
		dfs(0,0,0);
		System.out.println(count);
	}
	static int count=0;
	public static void dfs(int i,int kk,int sum) {
		if(kk==k) {
			if(isP(sum)) {
				count++;
			}
			return;
		}
		if(i==n) {
			return;
		}
		dfs(i+1,kk+1,sum+arr[i]);//选
		dfs(i+1,kk,sum); //不选
	}
	public static boolean isP(int n) {
		for(int i=2;i*i<=n;i++) {
			if(n%i==0) {
				return false;
			}
		}
		return true;
	}
}

(10)

(13)P1010 幂次方
这题在蓝桥杯练习系统中出现过 解析链接

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		f(n);
	}
	public static void f(int n) {
		if(n==0) {           //这3个if都是终止条件
			System.out.print("0");
			return;
		}
		if(n==1) {
			System.out.print("2(0)");
			return;
		}
		if(n==2) {
			System.out.print("2");
			return;
		}
		System.out.print(2);    //如果在上面if没有被终止,那么肯定n>2的,那么肯定要进行递归,所以这里先写个2
		int sum=1,pow=0;//sum最接近n的2的幂次方数,pow代表的幂方
		while(sum<=n) {
			pow++;
			sum*=2;
		}
		sum/=2;   //这里肯定是多算了一次,因为sum要大于n才停止循环
		pow--;
		if(pow==0||pow==2) {      //如果这个幂是0或者2可以直接出答案,如果是1则不用处理
			System.out.print("("+pow+")");
		}
		if(pow>=3) {  //如果大于3的话,肯定还要分解
			System.out.print("(");
			f(pow);
			System.out.print(")");
		}
		n-=sum;   //把n减掉最接近n的2的幂次方这个数
		if(n!=0) { //如果剩下的数不为0就要继续递归,并添置+号
			System.out.print("+");
			f(n);
		}
		
	}
}
发布了26 篇原创文章 · 获赞 18 · 访问量 4625

猜你喜欢

转载自blog.csdn.net/qq_43751506/article/details/105023670
1-4