【算法练习】蓝桥杯C++ AB组辅导课题单:第九讲(Java解答版)


鉴于近几年蓝桥杯DP题目数目增加,一定要对DP有较高的熟练度,并且能够秒杀经典题目。

一、复杂DP

1050、鸣人的影分身(中等)

在这里插入图片描述
注意N是包含鸣人自己在内的,所以不存在000的情况,M=7,N=3,可以分为:007,106,115,124,133,223,205,304,这个一看不就是搜索吗?DFS搜一下,过了。

import java.util.*;
import java.io.*;

public class Main {
	static int cnt = 0;
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		int t = scan.nextInt();
		while (t-- > 0) {
			int m = scan.nextInt();
			int n = scan.nextInt();
			cnt = 0;
			dfs(0, m, n);
			System.out.println(cnt);
		}
	}
	static void dfs(int start, int m, int n) {
		if (m < 0 || n < 0) return;
		if (n == 0 && m == 0) {
			cnt++;
			return;
		}
		for (int i = start; i <= m; i++) {
			dfs(i, m - i, n - 1);
		}
	}
}

本题更好的解法是DP,当然也更不好理解

在这里插入图片描述
把问题看作是放苹果问题,查克拉相当于苹果,影分身数量相当于盘子数。

import java.util.*;
import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		int t = scan.nextInt();
		int[][] dp;
		while (t-- > 0) {
			int m = scan.nextInt();
			int n = scan.nextInt();
			dp = new int[m + 1][n + 1];
			// m个苹果放n个盘子
			dp[0][0] = 1;
			for (int i = 0; i <= m; i++) {
				for (int j = 1; j <= n; j++) {
					// 当前盘子为空
					dp[i][j] = dp[i][j - 1];
					if (i >= j) {
						// 如果苹果数大于盘子数,那每一个盘子都能放一个,剩余i - j个苹果
						dp[i][j] += dp[i - j][j];
					}
				}
			}
			System.out.println(dp[m][n]);
		}
	}
}
1047、糖果(简单)

在这里插入图片描述
在这里插入图片描述
题目很简单,给你N个物品(每个物品装有不同的糖果,也就是重量),问你如何选择物品,能够得到最多的糖果(糖果数必须是K的倍数,也就是要求重量最大且重量是K的倍数)

一看这样子,先试试搜搜:

import java.util.*;
import java.io.*;

public class Main {
	static int[] candy;
	static LinkedList<Integer> tmp = new LinkedList<>();
	static int n, k;
	static int max = 0;
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		n = scan.nextInt();
		k = scan.nextInt();
		candy = new int[n];
		for (int i = 0; i < n; i++) candy[i] = scan.nextInt();
		dfs(0, 0);
		System.out.println(max);
	}
	static void dfs(int start, int sum) {
		if (sum != 0 && sum % k == 0) {
			max = Math.max(max, sum);
			return;
		}
		if (tmp.size() > n) return;
		for (int i = start; i < n; i++) {
			tmp.add(candy[i]);
			dfs(i + 1, sum + candy[i]);
			tmp.removeLast();
		}
	}
}

不行,超时,再来想想DP,看似背包问题,但是有k倍数的限制,该怎么处理呢?
在这里插入图片描述
难在对取余的处理,f[i][j]表示从 1 - i 包糖果里选,%k 为 j 的所有方案集合,不选第 i 包糖果好说:f[i - 1][j],拿了呢?应该转换为f[i - 1][j-a[i]],但是怎么处理余数,按照上面来。到最后:
( j − a [ i ] ) % k , j 本 来 就 是 k 的 余 数 , 所 以 还 是 等 于 j ( 也 可 以 就 写 成 那 样 ) 转 换 为 j − a [ i ] % , 但 是 j − a [ i ] % k 可 能 小 于 0 我 们 的 数 组 下 标 又 不 能 小 于 0 , 所 以 必 须 转 换 成 正 余 数 , 也 就 是 ( j + k − a [ i ] % k ) % k (j-a[i])\%{k},j本来就是k的余数,所以还是等于j(也可以就写成那样)\\转换为j - a[i] \%,但是j - a[i] \% k可能小于0\\我们的数组下标又不能小于0,所以必须转换成正余数,也就是(j + k - a[i] \% k) \% k (ja[i])%k,jkjja[i]%ja[i]%k00(j+ka[i]%k)%k

import java.util.*;
import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		int k = scan.nextInt();
		int[] candy = new int[n + 1];
		for (int i = 0; i < n; i++) candy[i + 1] = scan.nextInt();
		int[][] dp = new int[n + 1][k + 1];
		// dp[i][j] : 前i个糖果,总和%k为j的最大糖果数
		for (int i = 0; i < n; i++) {
			Arrays.fill(dp[i], Integer.MIN_VALUE);
		}
		dp[0][0] = 0;
		// dp[0][1] dp[0][2]...都没有意义
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j < k; j++) {
				// 余数最多取到k-1
				dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][(j - candy[i] % k + k) % k] + candy[i]);
			}
		}
		System.out.println(dp[n][0]);
	}
}
1222、密码脱落(中等)(最长回文子序列)

在这里插入图片描述
这道题是:给你一个字符串,让你找里面最长的回文子串长度,然后用总长度 - 该回文子串的长度就知道了需要脱落多少字符。

之前刷过的题目全都忘了…刚好把动态规划专题再复习一遍! 复习归来!

动态规划专题一

这道题就是求最长回文子序列的长度,然后用密码长度 - 最长回文子序列的长度,就可以得到需要脱落的字符:

import java.io.*;
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		String code = scan.nextLine();
		int n = code.length();
		int[][] dp = new int[n + 1][n + 1];
		// 找最长回文子串的长度(可以删除)
		for (int i = n; i >= 1; i--) {
			for (int j = i; j <= n; j++) {
				if (code.charAt(i - 1) == code.charAt(j - 1)) {
					if (i == j) dp[i][j] = 1;
					else if (j - i == 1) dp[i][j] = 2;
					else {
						dp[i][j] = dp[i + 1][j - 1] + 2;
					}
				} else {
					// 可以选择删除i、j
					dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
				}
			}
		}
		System.out.println(n - dp[1][n]);
	}
}

1220、生命之树(中等)(树状DP)

在这里插入图片描述
这是一道典型的树型DP问题,我们先来对树型DP问题有一个了解:
在这里插入图片描述

P1352、没有上司的舞会(树状DP)

在这里插入图片描述
题目很简单,如果父节点参加舞会,所有该父节点的子节点都不能参加舞会,每个人参加舞会都有一个快乐指数,问最大的快乐指数可以是多少?

对于树中某一个节点,它有两种状态:选它、不选它
在这里插入图片描述
站在根节点的位置,我们必须要直到儿子的情况,结合之前二叉树的相关知识,我们知道必须要在搜索完成儿子节点后才能考虑根节点值的变化。 还要使用vis数组避免重复访问树中节点。

还需要考虑初始情况,对于每一个节点,如果只考虑自身,那么f[node][1] = 该节点的快乐指数,因为选了该节点就是它自己了。

import java.io.*;
import java.util.*;

public class Main {
	static int[][] f;
	static boolean[] vis;
	static List<Integer>[] graph;
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		f = new int[n + 1][2];
		vis = new boolean[n + 1];
		for (int i = 2; i <= n + 1; i++) {
			// f[i][1] 代表 选择当前节点的快乐指数,目前是只考虑节点 i 的情况
			f[i - 1][1] = scan.nextInt();
		}
		boolean[] notRoot = new boolean[n + 1];
		graph = new LinkedList[n + 1];
		// 建树
		for (int i = 0; i < n + 1; i++) {
			graph[i] = new LinkedList<>();
		}
		for (int i = n + 2; i <= 2 * n; i++) {
			int l = scan.nextInt();
			notRoot[l] = true;
			int k = scan.nextInt();
			// k 是 l 的直接上司
			graph[k].add(l);
		}
		for (int i = 1; i <= n; i++) {
			if (notRoot[i] == false) {
				// 找到根节点,从根节点开始遍历
				dfs(i);
				System.out.println(Math.max(f[i][1], f[i][0]));
			}
		}
	}
	static void dfs(int k) {
		// 避免重复遍历
		vis[k] = true;
		for (int next : graph[k]) {
			// 遍历该节点的每个子节点
			if (vis[next]) continue;
			// 搜索
			dfs(next);
			// 我们需要直到f[next]的结果,所以必须要在搜索完成之后进行,也即后序位置进行
			// 如果根节点选了,子节点就只能不选
			f[k][1] += f[next][0];
			// 如果根节点没选,子节点就可以选、不选,由于有多个子结点,所以结果是累加
			f[k][0] += Math.max(f[next][0], f[next][1]);
		}
	}
}

再次强调DFS搜索的更新顺序:
在这里插入图片描述

P1122、最大子树和(树状DP)

在这里插入图片描述
对于当前节点,我们可以遍历于它相连接的节点,连接的节点可以选,也可以不选,考虑到节点的值可能为负数,所以当前节点的子树的值,要么就只含自己,要么就与其它节点相连接做累加。为避免负数的影响,需要每次和0相比。注意,至少要取一朵花。

还需要注意的是,本题并没有树的概念,更多表达的是一种图,所以建图是要注意双向边,并且从任意图的节点开始搜索都可以。

import java.io.*;
import java.util.*;

public class Main {
	static boolean[] vis;
	static List<Integer>[] graph;
	static int[] dp;
	static int max = Integer.MIN_VALUE;
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		dp = new int[n + 1];
		vis = new boolean[n + 1];
		// 记录每个花的漂亮值,也即是dp的初始值
		for (int i = 1; i <= n; i++) {
			dp[i] = scan.nextInt();
		}
		// 建树
		graph = new LinkedList[n + 1];
		for (int i = 0; i < n + 1; i++) {
			graph[i] = new LinkedList<>();
		}
 		for (int i = 1; i <= n - 1; i++) {
			int a = scan.nextInt();
			int b = scan.nextInt();
			// 注意无向边是双向边
			graph[a].add(b);
			graph[b].add(a);
		}
 		// 开始求解,从任意一个存在的节点开始求解都可以
 		dfs(1);
 		System.out.println(max);
	}
	static void dfs (int k) {
		vis[k] = true;
		for (int next : graph[k]) {
			if (vis[next]) continue;
			dfs(next);
			// 如果为负值,那就不要选了,剪枝掉
			dp[k] += Math.max(0, dp[next]);
		}
		max = Math.max(max, dp[k]);
	}
}

其实抛掉树型结构,抛掉一切外貌,只看本质,就是一个求最大子序列和的问题。

在这里插入图片描述
再回到本题,该如何求解?

和上一题类似,我们需要站在每个节点来看待问题,站在每个节点,看由当前节点作为根节点构成的子树的最大值,然后遍历完所有的可能子树,得到全局最大值。

import java.util.*;

public class Main {
	static List<Integer>[] graph;
	static long[] dp;
	static boolean[] vis;
	static long max = Long.MIN_VALUE;
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		dp = new long[n + 1];
		vis = new boolean[n + 1];
		for (int i = 1; i <= n; i++) {
			// 假设每个节点只选择自己,dp数组的值就是自己
			dp[i] = scan.nextInt();
		}
		graph = new LinkedList[n + 1];
		for (int i = 0; i < n + 1; i++) {
			graph[i] = new LinkedList<>();
		}
		for (int i = 0; i < n - 1; i++) {
			int u = scan.nextInt();
			int v = scan.nextInt();
			graph[u].add(v);
			graph[v].add(u);
		}
		// 相当于图,可以从任意节点开始搜索
		dfs(1);
		System.out.println(Math.max(0, max));
	}
	static void dfs(int k) {
		vis[k] = true;
		for (int next : graph[k]) {
			if (vis[next]) continue;
			dfs(next);
			// 对于每个父节点,可以选择选、不选子节点(选一定要能够增大子树和才选,所以需要和0比)
			dp[k] += Math.max(0L, dp[next]);
		}
		max = Math.max(max, dp[k]);
	}
}

树状DP将DP和DFS融合,比较考验对DP的推导,以及DFS的使用。遇到这类型树状DP,可以站在每个节点的角度进行思考,把每个节点作为根节点,建立当前跟节点的子树。

包子凑数

在这里插入图片描述
在这里插入图片描述
欧几里得定理:对于不完全为 0 的整数 a,b,gcd(a,b)表示 a,b 的最大公约数。那么一定存在整
数 x,y 使得 gcd(a,b)=ax+by。

扩展:如果有的包子种类的最大公约数不是1 那么凑不出来的情况就有无限多种。

当结论记住就行,如果需要凑数,可以用的数的gcd(两两求),不等于1的话,就不能凑出所有数。

import java.util.*;

public class Main {
    static int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        // 有多少数目的包子是凑不出来的
        int[] bz = new int[n];
        int flag = 0;
        boolean[] dp = new boolean[10001];
        dp[0] = true;
        for (int i = 0; i < n; i++) {
            bz[i] = scan.nextInt();
            dp[bz[i]] = true;
        }
        if (n == 1 && bz[0] != 1) {
            System.out.println("INF");
        } else if (n == 1 && bz[0] == 1) {
            System.out.println(0);
        } else {
            int tmp = bz[0];
            for (int i = 1; i < n; i++) {
                tmp = gcd(tmp, bz[i]);
            }
            // 最大公约数为 1 那么都可以凑出来
            if (tmp == 1) {
                flag = 1;
            }
            // 如果最大公约数不是 1,说明所有能够凑成的数都是某个数的倍数,INF
            if (flag == 0) {
                System.out.println("INF");
            } else {
                int ans = 0;
                for (int i = 0; i < n; i++) {
                    for (int j = 1; j <= 10000; j++) {
                        if (dp[j]) {
                            continue;
                        }
                        if (bz[i] <= j) {
                            dp[j] = dp[j - bz[i]];
                        }
                    }
                }
                for (int i = 1; i <= 10000; i++) {
                    if (dp[i]) {
                        continue;
                    }
                    ans++;
                }
                System.out.println(ans);
            }
        }
    }
}
1070、括号配对(中等)

在这里插入图片描述

POJ 2955 Brackets(简单)

先看一道与它类似的题目:POJ 2955 Brackets
在这里插入图片描述
意思是给你一个字符串,问你其中最长正则括号子序列的长度。
在这里插入图片描述
转自:https://www.cnblogs.com/fxh0707/p/14655368.html

其实和求最长回文的过程差不多,因为[()],这种情况,也就是枚举到的左右括号匹配时,就可以由f[i+1][j-1] + 2转换得来,这和求最长回文串是一样的,关键是左右括号不匹配时,该怎么转换,例如()(),并不是回文的形式,而是两个有效括号的拼接,如果还用上面的转移方程,结果=2,显然是错误的,答案应该是4。 该怎么办呢?我们漏掉了其它的匹配方式

枚举每一个可能拆分点,()(),可以拆分成(|)(),()|(),()(|),这三种情况对应了三种不同的区间拆分,解决了上述匹配方式不够全面的问题。f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j])

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
    	char[] str = reader.readLine().trim().toCharArray();
    	int len = str.length;
    	int[][] dp = new int[len + 1][len + 1];
    	for (int i = len; i >= 1; i--) {  // 注意转换方程跟最长回文一样,所以需要逆序遍历
    		for (int j = i + 1; j <= len; j++) {
    			// 先判断左右括号是否匹配
    			if ((str[i - 1] == '(' && str[j - 1] == ')') || (str[i - 1] == '[' && str[j - 1] == ']')) {
    				if (j - i == 1) dp[i][j] = 2;  // 跟最长回文一样,特判一下,避免下标交叉的情况
    				else dp[i][j] = dp[i + 1][j - 1] + 2;
    			}
    			// 然后按点拆分,以保证枚举完所有的情况
    			for (int k = i; k < j; k++) {
    				dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k + 1][j]);  // 因为是k + 1所以k只用枚举到j - 1
    			}
    		}
    	}
    	System.out.println(dp[1][len]);
    }
}

在这里插入图片描述
回到本题,本题需要你添加最少的字符数使得给出的BE成为GBE,也就是括号能够被匹配。这题问的是使所有括号完美匹配需要添加的最小括号数量。

要使添加的括号尽量少,我们需要使原来的括号序列尽可能多得匹配,先求最大匹配数量,那么还剩下一些没有匹配的括号,我们就需要依次加上一个括号使它们得到匹配。
在这里插入图片描述
状态方程的样子还是没变,只是其表达变了。如果枚举的当前左右括号匹配成功,例如:([]),那么至少需要添加的字符个数 = f[i + 1][j - 1]。但是如果是:()(),又该怎么办?同样的,括号问题我们都需要考虑这种连接的情况,解决这种问题的方法就是按不同节点进行拆分!f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j])。

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
    	char[] str = reader.readLine().trim().toCharArray();
    	int len = str.length;
    	int[][] dp = new int[len + 1][len + 1];
    	// 同样逆序遍历
    	for (int i = len; i >= 1; i--) {
    		for (int j = i; j <= len; j++) {
    			if (i == j) dp[i][j] = 1;  // 如果只有一个括号,无论如何都要添加一个字符
    			else {
    				// 找最小值,初始化为最大值
    				dp[i][j] = Integer.MAX_VALUE;
    				// 如果左右括号能够匹配,就取f[i + 1][j - 1]
    				if ((str[i - 1] == '(' && str[j - 1] == ')') || (str[i - 1] == '[' && str[j - 1] == ']')) {
    					if (j - i == 1) {
    						// 特判两个括号的情况
    						dp[i][j] = 0;
    					} else {
    						dp[i][j] = dp[i + 1][j - 1];
    					}
    				}
    				// 不管能否匹配,都需要按拆分点进行区间拆分
    				for (int k = i; k < j; k++) {
    					dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j]);
    				}
    			}
    		}
    	}
    	System.out.println(dp[1][len]);
    }
}
1078、旅游规划 (中等)

在这里插入图片描述

在这里插入图片描述
好样的!题都没看懂!

垒骰子(中等)

在这里插入图片描述
假设从上往下依次放骰子,最上面的骰子可以有6个面朝上,每个面朝上的基础上,侧面还有四个面可以旋转,所以对一个骰子而言,有4种可能。确定上一个骰子后,还需要考虑下一个骰子,考虑冲突面,也是可能有6个面朝上,但冲突面不能考虑,确定一个面后,侧面还有四个面4种可能,可以先给出递归代码(超时):

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static int[] to = new int[7];
    static boolean[][] conflict = new boolean[7][7] ;
    static int mod = 1000000007;
    public static void main(String[] args) throws IOException {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	int m = Integer.parseInt(input[1]);
    	// n个骰子,m个冲突
    	to[1] = 4;
    	to[2] = 5;
    	to[3] = 6;
    	to[4] = 1;
    	to[5] = 2;
    	to[3] = 6;
    	// 记录骰子的对面
    	while (m-- > 0) {
    		input = reader.readLine().trim().split(" ");
    		int a = Integer.parseInt(input[0]);
    		int b = Integer.parseInt(input[1]);
    		conflict[a][b] = true;
    		conflict[b][a] = true;
    	}	
    	// 对于最上面的一个骰子,可以六个面朝上
    	long ans = 0;
    	for (int i = 1; i <= 6; i++) {
    		ans += 4 * f(n - 1, i);  // 侧面有四个面可以旋转
    	}
    	System.out.println(ans);
    }
    static long f(int cnt, int up) {
    	// 剩余骰子数,上面是什么面
    	if (cnt == 0) {
    		// 没有骰子就return 1
    		return 1;
    	}
    	long ans = 0;
    	// 遍历下一个骰子的上面是什么面
    	for (int i = 1; i <= 6; i++) {
    		if (conflict[to[up]][i]) {
    			continue;  // 冲突了
    		}
    		ans += 4 * f(cnt - 1, i);  // 侧面有四个面可以旋转
    	}
    	return ans;
    }
}

我们知道了递归函数形式后,就可以试着推出动态规划的状态转移方程,dp[i][j] 代表第i个面朝上,j个骰子的方案数。所以dp[1][1]…dp[6][1] = 4,对于一个骰子而言,不管哪一面朝上都有4种方案(因为侧面可以旋转)。我们要求的答案ans = dp[1][n] + dp[2][n] + … + dp[6][n]。

dp[1][2] =4 * (dp[1][1] + dp[2][1] + … dp[6][1]),前提是两个骰子相接的面不冲突。

import java.util.Scanner;

public class Main {
    static int n, m;
    static int[] opp = new int[10];
    static boolean[][] conflict = new boolean[10][10];
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        m = scan.nextInt();
        // 记录骰子的对面
        opp[1] = 4;
        opp[2] = 5;
        opp[3] = 6;
        opp[4] = 1;
        opp[5] = 2;
        opp[6] = 3;
        // 记录冲突面
        int a, b;
        for (int i = 0; i < m; i++) {
            a = scan.nextInt();
            b = scan.nextInt();
            // 它们冲突了,注意
            conflict[a][b] = true;
            conflict[b][a] = true;
        }
        long ans = 0;
        // ans = dp[1][n] + dp[2][n] + dp[3][n] + ... + dp[6][n]
        // dp[1][1] = 4
        // dp[2][1] = 4
        // ...
        // dp[6][1] = 4
        // dp[i][j]:i朝上的面,j第几个骰子(从下往上)
        // dp[1][2] = (dp[1][1] + dp[2][1] + ... dp[6][1]) * 4
        long[][] dp = new long[7][n + 1];
        // 初始化,只有一个骰子,每个面朝上都有4种可能(因为侧面可以扭动)
        for (int i = 1; i <= 6; i++) {
            dp[i][1] = 4;
        }
        // i遍历骰子个数
        for (int i = 2; i <= n; i++) {
            // j遍历骰子的面
            for (int j = 1; j <= 6; j++) {
                // 遍历上一个骰子是否与当前骰子的面冲突
                for (int k = 1; k <= 6; k++) {
                    if (conflict[k][opp[j]]) {
                        continue;
                    }
                    dp[j][i] = (dp[j][i] + dp[k][i - 1]) % 1000000007;
                }
                dp[j][i] = (dp[j][i] * 4) % 1000000007;
                if (i == n) {
                    ans = (ans + dp[j][i]) % 1000000007;
                }
            }
        }
        System.out.println(ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值