目录
牛客.合唱团
牛客.kannan与高音
牛客.拜访(BFS)
牛客.买卖股票的最好时机(四)
牛客.合唱团
dp[i][j]:从1到i,中挑选最大乘积是多少,但是我们会发现状态转移方程推不出来,我们不知道如何推导的任意两个人,
从[1,i]中挑选,最大乘积是多少
dp[i][j]:从[1,i]中挑选,把j个人,最大乘积(arr[i]:必选,此时的一个最大乘积),因为我们知道获取这个状态值的时候,我们会知道,后面选当前位置的时候,前面那些人应该选择那哪些位置
f[i][j]:表示从[1,i]中挑选,挑选j个人,最后一个人必定选择,此时的最大乘积
g[i][j]:表示从[1,i]中挑选,最后一个人必选,此时的最小乘积。
返回值:
f[n][k],f[k][k]。
3.状态转移方程:
f[i][j]=Math.max(g[prev][j-1]*a[i],f[prev][j-1]*a[i]);
g[i][j]=Math.min(f[prev][j-1]*a[i],f[prev][j-1]*a[i]);
初始化
对角线以下,包括对角线
初始化:第一列会有可能越界,前i个里面选1个,且最后一个是必定选的,我们要记住,就好初始化了。
返回值
从f[k][k]-f[n][k]的max
import java.util.*; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); // 注意 hasNext 和 hasNextLine 的区别 int n = in.nextInt(); long[]arr=new long[55]; for(int i=1;i<=n;i++){ arr[i]=in.nextLong(); } int k= in.nextInt(); int d=in.nextInt(); long[][]f=new long[55][15]; long[][]g=new long[55][15]; //填写每一行 for(int i=1;i<=n;i++) { f[i][1]=g[i][1]=arr[i]; //假如i比较小的话,你从这么多k里面挑选不出来的是 for(int j=2;j<=Math.min(k,i);j++){ //初始化,里面都初始化为无穷 f[i][j]=-0x3f3f3f3f3f3f3f3fL; g[i][j]=0x3f3f3f3f3f3f3f3fL; //两个中的最小值,他这里的比较,你要知道是比较什么 f[I][j],保存的是上一个位置的值,因为从i个人里面选j个,的前一个状态是prev里面选j-1个,f[i][j]: 最后的比较是因为prev也在不断的变化,我们需要从i-d到,j-1到i-1保存一个最大的。f[i][j]看图 for(int prev=Math.max(i-d,j-1);prev<=i-1;prev++){ f[i][j]=Math.max(Math.max(f[prev][j-1]*arr[i],g[prev][j-1]*arr[i]),f[i][j]); g[i][j]=Math.min(Math.min(f[prev][j-1]*arr[i],g[prev][j-1]*arr[i]),g[i][j]); } } } long ret=-0x3f3f3f3f3f3f3f3fL; //f[k][k]-f[n][k]之间的最大值 for(int i=k;i<=n;i++){ ret=Math.max(ret,f[i][k]); } System.out.println(ret); } }
牛客.kannan与高音
开始的时候,我以为这个和之前那个最长递增子序列是一种类型,另外还是最优解,我直接想的就是用动态规划,但是发现动态规划无法使用,因为他是连续的子数组,而不是子序列,他是截取一段连续的序列,所以不可以是用什么dp[i][j]减去xx啥的。
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n = in.nextInt();
int[]a = new int[n + 1];
long max=1;
//dp[i][j]表示以j位置开启,i位置结束的最大长度
for (int i = 1; i <= n; i++) {
a[i] = in.nextInt();
}
for(int i=1;i<=n;){
int left=i;
int right=i+1;
long count = 1;
while(left<=n&&right<=n&&a[right]-a[left]<=8){
count++;
left++;
right++;
}
i=right;
max=Math.max(max,count);
}
System.out.print(max);
}
}
牛客.拜访(BFS)
单元最短路问题。
这是我做过比较难的bfs问题,需要存储的东西,如果你想不到这个表,那么就会比较麻烦了,所以我们需要这两张表,确实蛮复杂,比我想象的,但是摸索下来还是可以有一些思路的
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * @param CityMap int整型二维数组 * @param n int整型 * @param m int整型 * @return int整型 */ static int[]dx = {0, 0, 1, -1}; static int[]dy = {1, -1, 0, 0}; public int countPath (int[][] CityMap, int n, int m) { //从起点位置到当前点的最短距离 int[][]dist=new int[n][m]; //从起点位置到当前位置最短距离的方案长度 int[][]count=new int[n][m]; Queue<int[]>q = new LinkedList<>(); int ret=1; int[]d = new int[2]; for (int i = 0; i < n; i++) { Arrays.fill(dist[i],-1); } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (CityMap[i][j] == 2) { q.add(new int[] {i, j}); count[i][j]=1; dist[i][j] = 0; } else if (CityMap[i][j] == 1) { d[0] = i; d[1] = j; } } } while (!q.isEmpty()) { int x=0; int y=0; //sz代表每次进入q的组数,一次进两组的话,那么就这两组往上下左右遍历,遍历之后,最短的距离就+1 int sz = q.size(); while (sz != 0) { int[]t = q.poll(); for (int i = 0; i < 4; i++) { x = t[0] + dx[i]; y = t[1] + dy[i]; if (x >= 0 && x < n && y >= 0 && y < m && CityMap[x][y] != -1) { //这里这个dist[x][y]假如是-1,那么说明第一次过来,我们需要把这个值,赋值成当前距离 if (dist[x][y] == -1) { q.add(new int[]{x, y}); dist[x][y] = ret; //第一次到达这个位置,需要等于他的上一个位置,因为是第一次到达,所以这个数字也是初始值1。 count[x][y] = count[t[0]][t[1]]; } else { //观察是不是最短的距离。假如他是最短距离的话,那么他前面那个最短位置+1就会到目标位置,此时这个位置,就需要继承他的那个相当于父亲位置。 if (dist[t[0]][t[1]] + 1 == dist[x][y]) { count[x][y] += count[t[0]][t[1]]; } } } } sz--; } //ret这个值代表,从起点出发,到终点的最短距离,每次一组遍历,一个上下左右合记为一步,所以说,ret在最后++ ret++; } return count[d[0]][d[1]]; } }
牛客.买卖股票的最好时机(四)
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int n=in.nextInt();
int k=in.nextInt();
int[]prices=new int[n+1];
for(int i=0;i<n;i++){
prices[i]=in.nextInt();
}
//第i笔交易下第j天结束后最后一次状态是买入的最大收入
int[][]f=new int[n][k+1];
//第i笔交易下,第j天结束最后一次状态是卖出的最大收入
int[][]g=new int[n][k+1];
for(int i=0;i<=k;i++){
f[0][i]=-0x3f3f3f3f;
g[0][i]=-0x3f3f3f3f;
}
k=Math.min(k,n/2);
f[0][1]=-prices[0];
g[0][0]=0;
for(int i=1;i<n;i++){
for(int j=1;j<=k;j++){
f[i][j]=Math.max(f[i-1][j],g[i-1][j-1]-prices[i]);
g[i][j]=Math.max(g[i-1][j],f[i-1][j]+prices[i]);
}
}