2023java
排队
问题
Makik在上次的比赛后沉迷原神。现在要向你展示他给史莱姆排队的能力。
具体而言。我们用一个大写字母来表示一种史莱姆,
同种史莱姆之间没有区别,这里我们认为共有26种不同类型的史莱姆。
Makik有一个已经排好队的史莱姆序列E,
现在又给了你一个还没排好队的史莱姆序列A,
请问最少多少次操作能巴A排成B呢?这个问题太简单了,
所以 Makik,加了个限制条件:
每次移动A中的史莱姆的时候,
只能将恕要移动的史莱姆从原来的位置移动到队头,
也就是第一个史莱姆的前面。请问在这个限制下,
最少多少次操作可以将A排成B呢?
输入
10
SIAJOIWUGB
IBUSJGWAOI
输出
7
思路
在此代码中,我们使用一个二维数组dp来存储中间结果。
dp[i][j]表示将字符串A的前i+1个字符排列成字符串B的前j+1个字符
所需的最少操作次数。
我们使用两个嵌套循环来计算所有可能的子字符串长度和起始位置。
在每个子字符串中,如果首尾字符相同,则不需要进行任何操作,
因此dp[i][j]的值与dp[i+1][j-1]相同。如果首尾字符不同,
则我们需要将其中一个字符移到队头,这需要至少一次操作。
我们可以通过比较将第一个字符移到队头和将第二个字符
移到队头所需的操作次数来选择最佳操作。
最终结果存储在dp[0][n-1]中,其中n是字符串A的长度。
棋盘
题解
java版
package com.liujiabao.algorithm1;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public int M;
public int MAX_VA=Integer.MAX_VALUE;
public int[][] dp,dist;
public int[] distx=new int[]{-1,0,1,0},disty=new int[]{0,1,0,-1};
public Main(int M){
dp=new int[M+1][M+1];
dist=new int[M+1][M+1];
this.M=M;
for (int i=0;i<dist.length;i++){
Arrays.fill(dist[i],MAX_VA);
Arrays.fill(dp[i],-1);
}
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int m,n;
int x,y,value;
m=scanner.nextInt();
n=scanner.nextInt();
Main java22=new Main(m);
while ((n--)>0){
x=scanner.nextInt();
y=scanner.nextInt();
value=scanner.nextInt();
java22.setDP(x,y,value);
}
scanner.close();
java22.dfs(1,1,0,0);
if(java22.dist[m][m]== java22.MAX_VA){
System.out.println(-1);
}else {
System.out.println(java22.dist[m][m]);
}
}
public void setDP(int x,int y,int value){
dp[x][y]=value;
}
/*
* 几种情况
* 1.当超出棋盘时,不能转移,做可行性剪枝
* 2.当(nx,ny)无色时,再分两类
* 2.1 如果在(x,y)已经用过魔法,那么由于不能练习使用,不可转移
* 2.2 如果在(x,y)点没有用过魔法,那么可以使用魔法染色,花费2.
* 3.当(nx,ny)与(x,y)不同色时:转移,花费1
* 4.同色:花费0
* 但走到某个点的花费已经比目前最优解贵了,就不搜了
*
* */
public void dfs(int x,int y,int cost,int used){
if(dist[x][y]<=cost) return;
dist[x][y]=cost;
if(x==M&&y==M) return;
for(int i=0;i<4;i++){
int nx=x+distx[i],ny=y+disty[i];
if(nx<1||nx>M||ny<1||ny>M) continue;
if(dp[nx][ny]==-1){
if(used==1) continue;
else {
dp[nx][ny]=dp[x][y];
dfs(nx,ny,cost+2,1);
dp[nx][ny]=-1;
}
}
else if(dp[nx][ny] == dp[x][y]) dfs(nx,ny,cost,0);
else dfs(nx,ny,cost+1,0);
}
}
}
简单瞎搞题目(动态规划,bitset)
思路
一衍一
想了一天大致明白了,以上两位的思路有助于理解,上java代码(运行超时)。
package com.liujiabao.algorithm1;
import java.io.IOException;
import java.util.BitSet;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int NL=101*101*101;
BitSet bitSet=new BitSet(NL);
for(int j=0;j<1;j++){
int l=scanner.nextInt();
int r=scanner.nextInt();
for(int i=l;i<=r;i++){
bitSet.set(i*i);
}
}
for(int j=1;j<n;j++){
int l=scanner.nextInt();
int r=scanner.nextInt();
BitSet bitSet1=new BitSet(NL);
for(int i=l;i<=r;i++){
bitSet1.or(shiftLeft(bitSet,i*i));
}
bitSet=bitSet1;
}
System.out.println(bitSet.cardinality());
}
private static BitSet shiftLeft(BitSet bitset, int n) {
BitSet result = new BitSet(bitset.size() + n);
for (int i = 0; i < bitset.size(); i++) {
result.set((i + n) % bitset.size(), bitset.get(i));
}
return result;
}
}
戳气球
总结
区间dp
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// dp[i][j] = math.max ( dp[i][j], dp[i][k]+dp[k][j] + dp[i][k]*dp[k][k]*dp[k][j])
Scanner scanner=new Scanner(System.in);
int N=scanner.nextInt();
int[] nums=new int[N+2];
int[][] dp=new int[N+2][N+2];
for(int i=0;i<N+2;i++){
if(i==0||i==N+1){
nums[i]=1;
}else {
nums[i]=scanner.nextInt();
}
}
for(int i=N-1;i>=0;i--){
for(int j=i+2;j<=N+1;j++){
for(int k=i+1;k<j;k++) {
dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]);
}
}
}
System.out.println(dp[0][N+1]);
scanner.close();
}
}
//4
// 3 1 5 8
//
//167
树上自连
链接
两数相加
题解java
解题时多想
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
return add(false, l1, l2);
}
/**
* @param carry 是否进位
* @param l1 节点
* @param l2 节点
* @return
*/
public ListNode add(boolean carry, ListNode l1, ListNode l2) {
if (l1 == null && l2 == null && !carry) return null;
int a = l1 == null ? 0 : l1.val;
int b = l2 == null ? 0 : l2.val;
int sum = carry ? a + b + 1 : a + b;
return new ListNode(sum % 10 ,add(sum >= 10, l1 == null ? null: l1.next, l2 == null ? null : l2.next));
}
}
无重复字符的最长子串
i=-1;用的很巧妙
无重复最长子串
最长回文串
java代码实现
package com.liujiabao.algorithm2;
import java.util.ArrayList;
import java.util.Stack;
class Solution {
public static void main(String[] args) {
String s="a";
Solution solution=new Solution();
System.out.println(solution.longestPalindrome(s));
}
public String longestPalindrome(String s) {
int n=s.length();
boolean[][] dp=new boolean[n][n];
int maxLen=0;
int start=0;
/*
* 单个字符是回文串
* dp[i][j] 代表第 i个到第j个字符是回文串
* 从 len长度为 1 , 2 ,3,4....n依次往上判断是否为回文串
* 回文串长度有两种情况,一种是偶数,一种是奇数
* 所有先解决以上问题初始化:对每一个字符设置为true,对每一个字符相邻的字符比较看他们是否可以是回文串
*
* */
for(int i=0;i<n;i++){
dp[i][i] =true;
if(i<n-1&&s.charAt(i)==s.charAt(i+1)){
dp[i][i+1]=true;
maxLen=2;
start=i;
}
}
/*
* 开始从len:3,4,5,6,...n(因为一开始初始化已经解决了len:1,2)长度判定
* */
for(int len=3;len<=n;len++){
/*
* 既然你的len是从小往上,递增的
* 所有之前的不需要再比较了,所有i只需要比较到 i<n-len+1
* */
for(int i=0;i<n-len+1;i++){
int j=i+len-1;
if(s.charAt(i)==s.charAt(j)&&dp[i+1][j-1]){
dp[i][j]=true;
if(len>maxLen){
maxLen=len;
start=i;
}
}
}
}
// substring 切割的是 [i,i+len) ,右边是开区间
if(maxLen==0){
return s.substring(start,start+1);
}
return s.substring(start,start+maxLen);
}
}
盛最多水的容器
public class Solution {
public int maxArea(int[] height) {
int l = 0, r = height.length - 1;
int ans = 0;
while (l < r) {
int area = Math.min(height[l], height[r]) * (r - l);
ans = Math.max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
}
三数之和
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}
接近三数之和
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length;
int best = 10000000;
// 枚举 a
for (int i = 0; i < n; ++i) {
// 保证和上一次枚举的元素不相等
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 使用双指针枚举 b 和 c
int j = i + 1, k = n - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
// 如果和为 target 直接返回答案
if (sum == target) {
return target;
}
// 根据差值的绝对值来更新答案
if (Math.abs(sum - target) < Math.abs(best - target)) {
best = sum;
}
if (sum > target) {
// 如果和大于 target,移动 c 对应的指针
int k0 = k - 1;
// 移动到下一个不相等的元素
while (j < k0 && nums[k0] == nums[k]) {
--k0;
}
k = k0;
} else {
// 如果和小于 target,移动 b 对应的指针
int j0 = j + 1;
// 移动到下一个不相等的元素
while (j0 < k && nums[j0] == nums[j]) {
++j0;
}
j = j0;
}
}
}
return best;
}
}
优美的排列
class Solution {
List<Integer>[] match;
boolean[] vis;
int num;
public int countArrangement(int n) {
vis = new boolean[n + 1];
match = new List[n + 1];
for (int i = 0; i <= n; i++) {
match[i] = new ArrayList<Integer>();
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i % j == 0 || j % i == 0) {
match[i].add(j);
}
}
}
backtrack(1, n);
return num;
}
public void backtrack(int index, int n) {
if (index == n + 1) {
num++;
return;
}
for (int x : match[index]) {
if (!vis[x]) {
vis[x] = true;
backtrack(index + 1, n);
vis[x] = false;
}
}
}
}
判断通过操作能否让字符串相等 II
训练师最大匹配
内存超出
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Stack;
public class Solution {
public static void main(String[] args) {
int[] players=new int[]{1,1000};
int[] trainers=new int[]{1000,1};
Solution solution=new Solution();
System.out.println(solution.matchPlayersAndTrainers(players,trainers));
// 2
}
public int matchPlayersAndTrainers(int[] players, int[] trainers) {
// 长度
int leni=players.length;
int lenj=trainers.length;
// 图 i行代表第i名运动员,j列代表第j个训练师
int[][] grap=new int[leni+1][lenj+1];
// 保留第j名训练师训练的第i名运动员
int[] visitedi=new int[leni+1];
int[] visitedj=new int[lenj+1];
// 最大匹配数
int num=0;
// 循环匹配
for(int i=1;i<=leni;i++){
if(visitedi[i]>0){
continue;
}
for(int j=1;j<=lenj;j++){
if(players[i-1]<=trainers[j-1]){
if(visitedj[j]==0){
visitedi[i]=j;
visitedj[j]=i;
num++;
break;
}else if(players[i-1]>players[visitedj[j]-1]){
int temp=visitedj[j];
visitedi[visitedj[j]]=0;
visitedj[j]=i;
visitedi[i]=j;
i=temp-1;
break;
}
}
}
}
return num;
}
}
/*
* 通过二部图
* 匹配:
* 第i名运动员小于等于第j名训练师的能力值进行匹配,如果第k名运动员已经匹配了第j名训练师,判断i和k谁的能力值大小,大的匹配,小的进入未匹配状态
*
*
*
* */
双指针+贪心
class Solution {
public int matchPlayersAndTrainers(int[] players, int[] trainers) {
Arrays.sort(players);
Arrays.sort(trainers);
int m = players.length, n = trainers.length;
int count = 0;
for (int i = 0, j = 0; i < m && j < n; i++, j++) {
while (j < n && players[i] > trainers[j]) {
j++;
}
if (j < n) {
count++;
}
}
return count;
}
}
最小平均差
前缀和
用long保存
Java中的long是一种基本数据类型,它占用64位(8字节)的内存空间。long类型的范围从-9223372036854775808到9223372036854775807。在大多数情况下,int类型就足够用了,因为它的范围从-2147483648到2147483647,对于大多数应用来说已经足够了。然而,如果你需要处理非常大的数字,或者想要确保你的代码在所有情况下都能正常工作(例如,处理的数据可能大于int的最大值),那么使用long类型就很有必要。
class Solution {
public int minimumAverageDifference(int[] nums) {
int n = nums.length, res = 0;
long min = Integer.MAX_VALUE;
long[] sum = new long[n + 1];
for (int i = 0; i < n; i++) {
sum[i + 1] = sum[i] + nums[i];
}
for (int i = 0; i < n; i++) {
long diff = Math.abs(sum[i + 1] / (i + 1) - ((n - i - 1 == 0) ? 0 : (sum[n] - sum[i + 1]) / (n - i - 1)));
if (diff < min) {
res = i;
min = diff;
}
}
return res;
}
}
public int minimumAverageDifference(int[] nums) {
// 长度
int len=nums.length;
// 前缀和
long[] prefix_and = new long[len];
// 初始化
for(int i=0;i<len;i++){
prefix_and[i]=nums[i];
if(i>0){
prefix_and[i]+=prefix_and[i-1];
}
}
// 计算
long min=Integer.MAX_VALUE;
int index=-1;
long max=prefix_and[len-1];
for(int i=0;i<len;i++){
//
long temp;
if(len-i-1==0){
temp=Math.abs((prefix_and[i]/(i+1))-0);
}else {
temp=Math.abs((prefix_and[i]/(i+1))-((max-prefix_and[i])/(len-i-1)));
}
if(temp<min){
min=temp;
index=i;
}
}
return index;
}