第一题:P4017 最大食物链计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目分析:
1.根据“最大食物链”指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者可以知道当该生物不能捕食时,也就是入度为零(原始生物),当该生物不能被捕食时,也就是出度为零(食物链顶端)
2.用List集合创建一个图(graph)存储被捕食者与捕食者的关系
3.使用拓扑排序(定义:将有向图中的顶点以线性方式进行排序。即对于任何连接自顶点u到顶点v的有向边uv,在最后的排序结果中,顶点u总是在顶点v的前面),从原始生物开始,当找到食物链顶端生物时则进行答案存储
package 蓝桥算法训练__普及组.Day9;
import java.io.*;
import java.util.*;
/**
* @author snippet
* @data 2023-02-13
* P4017 最大食物链计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
*/
//拓扑排序
public class T1 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int mod = 80112002;
static int n,m,ans;// n表示生物的种类 m表示物种间的关系
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
// 用集合来存所有的数据 也就类似是一个图
List<List<Integer>> graph = new ArrayList<>();
int[] eat = new int[n];// 吃 入度
int[] deat = new int[n];// 被吃 出度
int[] line = new int[n];// 存第i种生物到原始生物 有多少条食物链
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
// 图里面的数据存入
for (int i = 0; i < m; i++) {
s = br.readLine().split(" ");
int de = Integer.parseInt(s[0])-1;
int e = Integer.parseInt(s[1])-1;
graph.get(de).add(e);
eat[e]++;
deat[de]++;
}
Queue<Integer> queue = new LinkedList<>();
// 记录是原始生物(不能吃其他生物的生物)的所有生物
for (int i = 0; i < n; i++) {
if (eat[i] == 0) {
queue.offer(i);
line[i] = 1;
}
}
// 从原始生物开始一个个遍历
while (!queue.isEmpty()) {
int temp = queue.poll();
// list集合中存了 能吃掉生物temp的生物种类的序号
List<Integer> list = graph.get(temp);
for (int k : list) {
line[k] += line[temp];
line[k] %= mod;
eat[k]--;
if (eat[k] == 0) {
// 该k生物不能被吃 也就是出度为零了 也就是食物链顶端了
if (deat[k] == 0) {
ans += line[k];
ans %= mod;
} else {
// 反之 则将该生物入队
queue.offer(k);
}
}
}
}
out.println(ans % mod);
out.flush();
}
}
![](https://img-blog.csdnimg.cn/img_convert/329ed3ad4fbf5a74f735c9420cc08e21.png)
第二题:P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目分析:
求卒从A到B的时候,在马的拦路情况下的路径条数,需要用一个二维数组m来存马可以到达的位置,可以到达则为true
动态规划,更新到达i,j位置时的路径条数,因为卒只可以往右走和往下走,所以dp的时候dp[i][j]=dp[i-1][j]+dp[i][j-1]
为了防止数组越界,所有坐标数+2
初始化dp数组的时候注意,根据分析2可以知道我们在初始化dp数组的时候满足dp[1][2]=1和dp[2][1]=1两个中的其中一个即可
package 蓝桥算法训练__普及组.Day9;
import java.io.*;
/**
* @author snippet
* @data 2023-02-13
* P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
*/
// 动态规划
public class T2 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
// bx,by是卒的终点位置 cx,cy是马的初始位置
static int bx,by,cx,cy;
// 马的行动路径
static int[] dx = {0, -1, 1, -2, 2, -2, 2, -1, 1};
static int[] dy = {0, -2, -2, -1, -1, 1, 1, 2, 2};
// 数组dp用来存i,j位置的路径条数 数组m用来存i,j位置是否被马拦住了
static long[][] dp = new long[25][25];
static boolean[][] m = new boolean[25][25];
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
// 坐标+2 防止数组越界
bx = Integer.parseInt(s[0]) + 2;
by = Integer.parseInt(s[1]) + 2;
cx = Integer.parseInt(s[2]) + 2;
cy = Integer.parseInt(s[3]) + 2;
// 初始化dp数组 因为卒走第一步需要从左边或者上面走一步过来
// 所以我们需要满足(dp[1][2]=1)和dp([2][1]=1)两个中的其中一个即可
dp[1][2] = 1;
// 标记马可以到达的所有位置
for (int i = 0; i < 9; i++) {
m[cx+dx[i]][cy+dy[i]] = true;
}
// 动态规划 修改位置的路径条数
// 因为卒只可以往下走和往右走
// 所以位置只可以从上面或者左边走过来
for (int i = 2; i <= bx; i++) {
for (int j = 2; j <= by; j++) {
if (m[i][j]) continue;
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
out.println(dp[bx][by]);
out.flush();
}
}
![](https://img-blog.csdnimg.cn/img_convert/527d466d0564637d1060f64b1d1118e4.png)
第三题:P1434 [SHOI2002] 滑雪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目分析:
求最长的滑雪距离,根据题目一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小,表示只有当当前高度大于下一滑雪点的高度才可以转移
因为求最长的滑雪距离,则应先求出所有位置自己的最长滑雪距离,再求所有位置的最长滑雪距离
在搜索的时候,有些点可以已经搜索过了,使用记忆化搜索,如果当前点未被搜索过,则给这个点记上标记(直接赋值滑雪距离为1),如果当前点已经被搜索过,则直接返回该点的最长滑雪距离
package 蓝桥算法训练__普及组.Day9;
import java.io.*;
/**
* @author snippet
* @data 2023-02-13
* P1434 [SHOI2002] 滑雪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
*/
//记忆化搜索 dfs
public class T3 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int r,c,max,ans;
// 滑雪可选择的转向
static int[] dx = { 1, 0, -1, 0};
static int[] dy = { 0, -1, 0, 1};
// 数组a用来存所有的滑雪高度
static int[][] a = new int[110][110];
// 数组f用来记录该点的最长滑雪路线
static int[][] f = new int[110][110];
static int dfs(int x, int y) {
// 记忆化搜索 如果该点已经搜索过 则直接return该点记录的最长滑雪路径
if (f[x][y] != 0) return f[x][y];
// 第一次搜索该点时 给其赋值
f[x][y] = 1;
// 遍历x,y位置的上下左右每个位置
for (int i = 0; i < 4; i++) {
int x1 = x + dx[i];
int y1 = y + dy[i];
// 满足条件的情况下 递归x1,y1这个点 再回溯求x,y这个点的最大滑行距离
if (x1 > 0 && y1 > 0 && x1 <= r && y1 <= c && a[x][y] > a[x1][y1]) {
dfs(x1, y1);
f[x][y] = Math.max(f[x][y], f[x1][y1]+1);
}
}
return f[x][y];
}
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
r = Integer.parseInt(s[0]);
c = Integer.parseInt(s[1]);
for (int i = 1; i <= r; i++) {
s = br.readLine().split(" ");
for (int j = 1; j <= c; j++) {
a[i][j] = Integer.parseInt(s[j-1]);
}
}
// 需要所有数据都读入之后 才能进行搜索
for (int i = 1; i <= r; i++) {
for (int j = 0; j <= c; j++) {
// 求每个位置的最长滑行距离 dfs(i,j)
// 同时
// 求所有位置的最长滑行距离的最大值 max(ans, dfs(i,j))
ans = Math.max(ans, dfs(i,j));
}
}
// for (int i = 1; i <= r; i++) {
// for (int j = 1; j <= c; j++) {
// out.print(a[i][j] + " ");
// }
// out.println();
// }
out.println(ans);
out.flush();
}
}
![](https://img-blog.csdnimg.cn/img_convert/0fe8b588fb3312aa731dd0d5c71a6f12.png)